DEV Community

Cover image for Angular HostAttributeToken: the new way to inject attributes
Davide Passafaro
Davide Passafaro

Posted on • Originally published at codemotion.com

Angular HostAttributeToken: the new way to inject attributes

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:

<my-divider size="small" />
Enter fullscreen mode Exit fullscreen mode

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

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;
}
Enter fullscreen mode Exit fullscreen mode

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

Meme - Well done. BUT...

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:

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) {}
}
Enter fullscreen mode Exit fullscreen mode

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!

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:

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') );
}
Enter fullscreen mode Exit fullscreen mode

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. 🔥

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. 👋😁

Top comments (0)