Combining Angular Directives with the Directive Composition API

Posted: 12/18/2022
Categories: angular

The Directive Composition API brings with it a couple benefits:

It's the second benefit that this post focuses on.

If you are unfamiliar with the Directive Composition API, you can read my introduction.

The Scenario

Across our app, we have a set of forms field components. To assist with consistency and code re-use, we've created the following directives to apply to those fields:

Sometimes we need some of those, but on other occasions we need them all.

This is one example of where the Directive Composition API can come in handy.

Using the Directive Composition API we can create a directive that combines all those directives.

How?

Making the Component Standalone

First of all, we need to make sure that each of the directives are standalone.

To do this, first add the standalone property in the directive's decorator and set it to true:

@Directive({
selector: '[appMyDirective]',
standalone: true
})

You then need to remove your directive from a module's declarations array if it features in one.

Creating your Composite Directive

The act of creating a composite directive is actually rather simple.

Create a directive that will eventually combine the other directives:

@Directive({
selector: "[appStandardFormLabel]",
standalone: true,
})
export class StandardFormLabelDirective {}

We can now add the hostDirectives property to our directive to include the directives we want it to apply:

@Directive({
selector: "[appStandardFormLabel]",
standalone: true,
hostDirectives: [
InvalidFieldDirective,
RequiredFieldDirective,
TooltipDirective,
],
})
export class StandardFormLabelDirective {}

Now that we have our composite directive, we can put it to use:

<app-form-label app-standard-form-label></app-form-label>

We have a problem though: some of our directives have inputs that are required:

LIST THE DIRECTIVES AND THEIR INPUTS HERE

We can solve this by specifying the inputs for each of the directives:

EXAMPLE

We then pass the inputs in as we would any other directive:

EXAMPLE