Directive Composition with Angular 15
Tags: webdev, angular, news, javascript
Published: 2022-11-09 14:06:49

## Introduction

With a release cycle every 6 months, the release of Angular 15 is fast approaching.
As a reminder, the version 14 of Angular brought some big new features:
- standalone components
- composition pattern
- typed forms, etc

The standalone components have had a big impact on the entire Angular ecosystem. And this feature still has impacts in version 15.
In this version, a new API will be availble: the **directive composition API**

## The problem

Directives are one of the most important and powerful parts of Angular. There are 3 types of directives:
- structural directives allowing to structure the DOM
- attribute directives allowing you to enrich an element of the DOM
- components which are simple directives without views

A concrete example of a directive could be the MatTooltip directive of Angular Material. This directive allows to display additional information to the user when he hovers over a text for example.

```html
<div matTooltip="More information" matTooltipPosition="left">
  This is a text
</div>
```

Now imagine that you want to create a button that displays additional information when the user hovers over it.

If we take inspiration from the various libraries in Angular, it's a good practice to write an attribute directive that will enrich the classic button

```html
<button type="button" sfeirButton matTooltip="awesome button" matTooltipPosition="left">
  Click Me
</button>
```

This case is too boilerplate. Developers should remember to use the MatTooltip directive and configure it correctly.

Ideally it will be awesome if developers can write someting like this:

```html
<button type="button" sfeirButton tooltipText="awsome button" tooltipPosition="left">
  Click Me
</button>
```

If we want to write this kind of code, we have two choices:
- extend the MatTooltip class, but it's only possible to extend one class, which limits the possibilities of composition
- to realize a mixins pattern present in Vue Js which can be complicated to maintain due to the possible side effects. For information Angular material uses this pattern as we can see [here](https://github.com/angular/components/blob/03408fdb83680cbb69f5d547437e520910a905a3/src/material/tabs/tab.ts)

## Solution

To solve this problem, and always with the aim of improving the developer experience, the Angular team introduces a new API called: **Directive Composition API**.

This API will allow to compose the directives between them in a very simple way.

Now the _@Directive_ and _@Component_ decorator take a new property called hostDirectives.

This property takes as value an array of standalone directives or an array of configuration objects

```typescript
@Directive({
  selector: "button[sfeirButton]"
  hostDirectives: [
    AwesomeDirective,
    { directive: MatTooltip,
      inputs: [],
      outputs: []
    }
  ]
})
export class SfeirButton
```

By default none of the inputs or outputs of the host directives will be available on the host, unless they are specified in the inputs or outputs properties.

If you want to expose one of the inputs or outputs of the host directives, you must specify it respectively in the inputs and outputs array.

```typescript
@Directive({
  selector: "button[sfeirButton]"
  hostDirectives: [
    { directive: MatTooltip,
      inputs: ['matTooltip', 'matTooltipPosition'],
      outputs: []
    }
  ]
})
export class SfeirButton
```

The API also allows you to rename the exposed inputs and outputs

```typescript
@Directive({
  selector: "button[sfeirButton]"
  hostDirectives: [
    { directive: MatTooltip,
      inputs: [
        'matTooltip: tooltipText',
        'matTooltipPosition: tooltipPosition
      ],
      outputs: []
    }
  ]
})
export class SfeirButton
```

Thanks to this API, and with this way, it's possible now to write:

```html
<button type="button" sfeirButton tooltipText="awsome button" tooltipPosition="left">
  Click Me
</button>
```

Obviously this syntax is also valid for the outputs

One last important thing, the host directives are picked by view/content queries as well, so it's possible to write:

```typescript
@ViewChild(MatToolTip) matToolTip!: MatTooltip
```

## Conclusion

With version 15 and the new API, it becomes very easy to compose directives together.
To compose directives between them, you just have to use the new hostDirective property.
Be careful to compose directives between them, all directives must be of type standalone.