-
Book Overview & Buying
-
Table Of Contents
-
Feedback & Rating

Angular Cookbook
By :

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.
The app that we are going to work with resides in start/apps/chapter02/ng-directive-comp-api
inside the cloned repository:
npm run serve ng-directive-comp-api
This 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
start
folder 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=false
If asked, choose the @nx/angular:component schematics
and 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 NgModule
and the AppComponent
is not a standalone component. Therefore, we these directives to be imported in the app.module.ts
for this recipe to work.
ButtonDirective
a standalone directive, which means this isn’t going to be a part of any NgModule
. Update the button.directive.ts
as follows:
...
@Directive({
selector: '[appButton]',
standalone: true,
})
export class ButtonDirective {
...
}
app.module.ts
file as it is now a standalone
directive. Update the app.module.ts
file 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
ButtonFilledDirective
to use the ButtonDirective
using the Directive Composition API. Update the button-filled.directive.ts
file 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';
}
appButtonFilled
directive in the app.component.html
file 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.
ButtonOutlined
directive as well. We’ll modify the button-outlined.directive.ts
as 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';
}
ButtonWithTooltipDirective
class. We’ll update the button-with-tooltip.directive.ts
as 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 TooltipDirective
is not a standalone component. That’s true. We need to do the same thing we did for the ButtonDirective
in step 2 and step 3 for the TooltipDirective
as well. Move on to the next step once you’ve done that.
app.component.html
file to use both the appButtonOutlined
and appButtonTooltip
directives 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
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:
AppComponent
uses the ButtonFilledDirective
, ButtonOutlinedDirective
, and ButtonWithTooltipDirective
directives. For this, these directive need to be non-standalone since the application is bootstrapped with an NgModule
ButtonFilledDirective
, ButtonOutlinedDirective
, and ButtonWithTooltipDirective
directives use the directive composition API to use the ButtonDirective
and the TooltipDirective
. 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
],
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
Change the font size
Change margin width
Change background colour