Applying multiple directives to the same element using the Directive Composition API
In this recipe, you’ll use the Directive Composition API to create multiple components and apply directives to them directly for reusability instead of having to apply the directives to each component or create additional elements inside the template of the component to apply the directives.
Getting ready
The app that we are going to work with resides in start/apps/chapter02/ng-directive-comp-api inside the cloned repository:
- Open the code repository in your code editor.
- Open the terminal, navigate to the code repository directory, and run the following command to serve the project:
npm run serve ng-directive-comp-apiThis should open the app in a new browser tab, and you should see the following:

Figure 2.13: ng-directive-comp-api app running on http://localhost:4200
How to do it…
- First, we’ll create a couple of components for our application. We’ll create one directive for the filled button, one for the outline button, and one for a button with a tooltip. Run the following command from the
startfolder within the workspace:nx g directive button-filled --directory apps/chapter02/ng-directive-comp-api/src/app/directives --standalone=false nx g directive button-outlined --directory apps/chapter02/ng-directive-comp-api/src/app/directives --standalone=false nx g directive button-with-tooltip --directory apps/chapter02/ng-directive-comp-api/src/app/directives --standalone=falseIf asked, choose the
@nx/angular:component schematicsand choose the “As provided” action.Note that all the directives we have created are non-standalone directives. That is because the application is bootstrapped with an
NgModuleand theAppComponentis not a standalone component. Therefore, we these directives to be imported in theapp.module.tsfor this recipe to work.
- Let’s make the
ButtonDirectivea standalone directive, which means this isn’t going to be a part of anyNgModule. Update thebutton.directive.tsas follows:... @Directive({ selector: '[appButton]', standalone: true, }) export class ButtonDirective { ... } - Let’s also remove it from the
app.module.tsfile as it is now astandalonedirective. Update theapp.module.tsfile as follows:... import { ButtonDirective } from './directives/button.directive'; // <-- remove the import ... @NgModule({ declarations: [ ..., ButtonDirective, // <-- remove this ... ], ... }) export class AppModule {}You’ll notice that none of the buttons have the required styles anymore as follows:

Figure 2.14: Styles from the button directive are gone
- Let’s update the
ButtonFilledDirectiveto use theButtonDirectiveusing the Directive Composition API. Update thebutton-filled.directive.tsfile as follows:import { Directive, HostBinding } from '@angular/core'; import { ButtonDirective } from './button.directive'; @Directive({ selector: '[appButtonFilled]', hostDirectives: [ { directive: ButtonDirective, inputs: ['color'], }, ], }) export class ButtonFilledDirective { @HostBinding('attr.fill') fill = 'filled'; } - We can use the
appButtonFilleddirective in theapp.component.htmlfile as follows:... <main class="content" role="main"> <ul class="flex flex-col"> <li class="flex gap-4 items-center border-b justify- between border-slate-300 py-3">...</li> <li class="flex gap-4 items-center border-b justify- between border-slate-300 py-3"> <h4 class="text-lg">Filled Button:</h4> <button appButtonFilled color="yellow">Click Me</button> </li> <li class="flex gap-4 items-center border-b justify- between border-slate-300 py-3">...</li> <li class="flex gap-4 items-center border-b justify- between border-slate-300 py-3">...</li> </ul> </main>
Notice that we’ve removed the fill attribute from the element.
- Let’s update the
ButtonOutlineddirective as well. We’ll modify thebutton-outlined.directive.tsas follows:import { Directive, HostBinding } from '@angular/core'; import { ButtonDirective } from './button.directive'; @Directive({ selector: '[appButtonOutlined]', hostDirectives: [ { directive: ButtonDirective, inputs: ['color'], }, ], }) export class ButtonOutlinedDirective { @HostBinding('attr.fill') fill = 'outlined'; } - Let’s also modify the
ButtonWithTooltipDirectiveclass. We’ll update thebutton-with-tooltip.directive.tsas follows:import { Directive } from '@angular/core'; import { ButtonDirective } from './button.directive'; import { TooltipDirective } from './tooltip.directive'; @Directive({ selector: '[appButtonWithTooltip]', hostDirectives: [ { directive: ButtonDirective, inputs: ['color', 'fill'], }, { directive: TooltipDirective, inputs: ['appTooltip: tooltip'], }, ], }) export class ButtonWithTooltipDirective {}You will notice that the app starts throwing an error that
TooltipDirectiveis not a standalone component. That’s true. We need to do the same thing we did for theButtonDirectivein step 2 and step 3 for theTooltipDirectiveas well. Move on to the next step once you’ve done that.
- Now, update the
app.component.htmlfile to use both theappButtonOutlinedandappButtonTooltipdirectives as follows:... <main class="content" role="main"> <ul class="flex flex-col"> <li class="flex gap-4 items-center border-b justify- between border-slate-300 py-3">...</li> <li class="flex gap-4 items-center border-b justify- between border-slate-300 py-3">...</li> <li class="flex gap-4 items-center border-b justify- between border-slate-300 py-3"> <h4 class="text-lg">Outlined Button:</h4> <button appButtonOutlined>Click Me</button> </li> <li class="flex gap-4 items-center border-b justify- between border-slate-300 py-3"> <h4 class="text-lg">Button with Tooltip:</h4> <div class="flex flex-col gap-4"> <button appButtonWithTooltip tooltip="code with ahsan" fill="outlined" color="blue"> Click Me </button> <button appButtonWithTooltip tooltip="code with ahsan" fill="filled" color="blue"> Click Me </button> </div> </li> </ul> </main>If you’ve followed all the steps correctly, you should be able to see the final result as follows:

Figure 2.15: Final result containing buttons with different directives applied
How it works…
The Directive Composition API was introduced in Angular v15 and has been one of the most requested features from the Angular community. In this recipe, we tried to create some components that bind the directives to the component directly in the component’s TypeScript classes rather than in the template. This eliminates the need to create a wrapper element within the components to then apply the directives or to map the inputs of the components to the inputs of the directives. This also allows multiple directives to be bound to the same component – even if they may have inputs with the same names, we can alias them differently.
The flow of the directives in our application works in the following way:
- The
AppComponentuses theButtonFilledDirective,ButtonOutlinedDirective, andButtonWithTooltipDirectivedirectives. For this, these directive need to be non-standalone since the application is bootstrapped with anNgModule ButtonFilledDirective,ButtonOutlinedDirective, andButtonWithTooltipDirectivedirectives use the directive composition API to use theButtonDirectiveand theTooltipDirective. These need to be standalone directives to be used as ‘hostDirectives'
The key to using the Directive Composition API is to construct your base-directives with the standalone: true flag. This means your directives aren’t part of any NgModule and can be imported directly into the imports array of any component they’re being used in. This is why we make both the ButtonDirective and the TooltipDirective standalone in steps 2, 3, and 7. Then, we use those directives in ButtonFilledDirective, ButtonOutlinedDirective, and ButtonWithTooltipDirective to be able to reuse the logic without having to create any wrapper component or additional HTML. We do it using the hostDirectives property in the directive metadata. Notice that we pass an array of objects to this property and each object can contain the directive property, which takes the class of the directive to be applied. And we can also provide inputs and outputs for the host bindings. As you saw for the ButtonWithTooltipDirective, we also aliased the appTooltip input of the TooltipDirective with the tooltip input of the ButtonWithTooltipDirective. One thing to notice is that if you don’t want to map any inputs or outputs and just want to bind a directive in the hostDirectives, you can just provide an array of the classes of the directives to be applied as follows:
hostDirectives: [
ButtonDirective,
TooltipDirective
],
See also
- Directive Composition API documentation: https://angular.io/guide/directive-composition-api#directive-composition-api
- Standalone components: https://angular.io/guide/standalone-components
Learn more on Discord
To join the Discord community for this book – where you can share feedback, ask questions to the author, and learn about new releases – follow the QR code below:
https://packt.link/AngularCookbook2e
