Angular HostAttributeToken: the new way to inject attributes
Tags: angular, frontend, webdev, javascript
Published: 2024-04-21 22:00:00

Over the latest months **Angular** has been evolving rapidly, introducing new features and a ton of improvements, many of these with the aim of improving maintainability and performance.

In this article I want to introduce you to the latest addition brought by **Angular v17.3.0**: the new **HostAttributeToken** class.

This new API allows you to inject host attributes using the **`inject()` function**, similar to the functionality of the **@Attribute** decorator.

The **@Attribute** decorator is perhaps the lesser-known Angular decorators.
Therefore, before delving into how the **HostAttributeToken class** works, let’s take a moment to review the fundamentals.

---

## @Attribute: do you really need an input?

Let’s start from a simple scenario in which you want to develop a component requiring a **configurable property**.

Specifically, you want to provide this property as a **constant string literal**, because it will not change throughout runtime:

```html
<my-divider size="small" />
```

What you would normally do is to create an **input** for that property:

```ts
import { Component, Input } from '@angular/core';

export type DividerSize = 'small' | 'big';

@Component({
  selector: 'my-divider',
  template: '<hr [class]="size" />',
  standalone: true,
})
export class MyDivider {
  @Input() size: DividerSize;
}
```

Which is fine and works quite well. Perhaps too much…


![Meme - Well done. BUT...](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z7if8wf9uvxma088sl8s.png)

By creating an input, you are instructing **Angular** to create a binding to that property and check it during each change detection cycle.

That is really excessive, you only need that property to be **checked once** during component initialization. 🤯

To make this more performant you can instead inject the **host-element attribute** in the **constructor** thanks to the **@Attribute** decorator:

```ts
import { Attribute, Component } from '@angular/core';

export type DividerSize = 'small' | 'big';

@Component({
  selector: 'my-divider',
  template: '<hr [class]="size" />',
  standalone: true,
})
export class MyDivider {
  constructor(@Attribute('size') size: string) {}
}
```

Using **@Attribute**, Angular will read the value only **once** and never again.

> _**Note:** This approach works as well with **Directives**, feel free to give it a shot!!!_

---

Now that we have covered the basics, let me introduce you to the main topic of this article: new **HostAttributeToken** class.


![Meme - Finally, let's talk about new stuff!](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c99124za1r8d580zia3g.png)

## Inject an Attribute using HostAttributeToken class

Since the release of Angular 14 there is now a cleaner approach to inject providers without using the constructor class: the **`inject()`** function.

Up until now, this **`inject()` function** allowed to **inject** easily components, directives and pipes, but it was missing a method to inject host attributes.

And that’s precisely why the **HostAttributeToken class** was introduced:

```ts
import { Component, HostAttributeToken, inject } from '@angular/core';

export type DividerSize = 'small' | 'big';

@Component({
  selector: 'my-divider',
  template: '<hr [class]="size" />',
  standalone: true,
})
export class MyDivider {
  size: string = inject( new HostAttributeToken('size') );
}
```

By injecting an **HostAttributeToken class** within the **inject function**, you get the corresponding **host-element attribute** value.

This new API works similarly to **@Attribute**, with a notable difference: instead of returning **`null`** when the attribute is missing, it throws an **error**.

![🔥 Error: NG0201: No provider for HostAttributeToken size found. 🔥](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hnl2p38wmoj5ik8doik0.png)

This change was made to align the behavior with other injection tokens.

---

## Thanks for reading so far 🙏

I’d like to have your feedback so please leave a **comment**, **like** or **follow**. 👏

Then, if you really liked it, **share it** among your community, tech bros and whoever you want. And don’t forget to **[follow me on LinkedIn](https://www.linkedin.com/in/davide-passafaro/)**. 👋😁