In this tutorial, we will explore all the ways to read HTML attribute values passed in the component or directive.
Reading HTML Attributes
Elements in HTML have attributes; these are additional values that configure the elements or adjust their behavior in various ways to meet the criteria the users want. - MDN Docs
For example:
<input type="text">
<td colspan="3"></td>
type
, and colspan
are some examples of HTML attributes.
In some cases, you need to handle the behaviour of a component or directive based on a value set for the HTML attribute.
For example, there is a component called smart-input
. You want to render different layouts for different type
s passed as values to the HTML attribute.
<!-- should render UI for text -->
<smart-input type="text"></smart-input>
<!-- should render UI for number -->
<smart-input type="number"></smart-input>
ElementRef
Class
One way to read such attribute values, is through ElementRef
:
import { Component, ElementRef } from '@angular/core';
@Component({
selector: 'smart-input',
template: `
<div [ngSwitch]="type">
<input type="text" *ngSwitchCase="'text'" />
<input type="number" *ngSwitchCase="'number'" />
<p *ngSwitchDefault>Unsupported type: {{ type }}</p>
</div>
`
})
export class SmartInputComponent {
type: string;
constructor(private elementRef: ElementRef) {
this.type = this.elementRef.nativeElement.getAttribute('type');
}
}
Above is fine and works as expected. But, there is a shorter way available for the above in Angular.
The @Attribute()
Parameter Decorator
You can use the Attribute
parameter decorator to pass the value of the HTML attribute to the component or directive constructor through dependency injection.
So for an earlier example, to read the type
attribute, we will do something like below:
import { Attribute, Component } from '@angular/core';
@Component({
selector: 'smart-input',
template: `...`
})
export class SmartInputComponent {
constructor(@Attribute('type') public type: string) {}
}
With the above code, smart-input
will render
-
<input type="text">
for<smart-input type="text"></smart-input>
-
<input type="number">
for<smart-input type="number"></smart-input>
Usage with providers
@Attribute()
decorator can also be used in deps
when making a FactoryProvider
.
So, for smart-input
, we can also write our code like below:
import { Component, Inject, InjectionToken, Attribute } from '@angular/core';
export const TOKEN = new InjectionToken<string>('TypeAttribute');
export function factory(token: string): string {
return token;
}
@Component({
selector: 'smart-input',
template: `...`,
providers: [
{
provide: TOKEN,
deps: [[new Attribute('type')]],
useFactory: factory
}
]
})
export class SmartInputComponent {
constructor(@Inject(TOKEN) readonly type: string) {}
}
Usage in other built-in directives
This decorator is also used by few built-in directives:
-
RouterLink
readstabindex
-
RouterOutlet
readsname
-
ngPluralCase
reads self-value
Limitations
Below are two limitations of using @Attribute()
decorator:
- It only works with static HTML attribute values. It doesn’t work with attribute binding and property binding.
- As it only works with static values, only
string
type is supported.
To overcome the above limitations, we can use @Input()
decorator.
The @Input()
Property Decorator
@Input()
is a property decorator, used in the child component or directive, which signifies that property can receive its value from the parent component.
So, for the above example, we can rewrite it as below:
@Component({
selector: 'smart-input',
template: `...`
})
export class SmartInputComponent {
@Input() type: string;
}
Above works fine. And unlike @Attribute()
, this supports all data-types and if you use property binding or text interpolation, it also tracks changes to the values.
Also note that, @Input()
works with static and dynamic, both bindings.
Usage in other built-in directives
This decorator is also used by many built-in directives. Below are a couple of examples:
Limitation
The only limitation of using @Input()
decorator is that it’s value is available only after component or directive is initialized, i.e. in ngOnInit
life-cycle hook.
ElementRef
/ @Attribute()
vs @Input()
We learned three ways to read type
’s value for oursmart-input
component, how they’re used in some built-in directives and limitations. Let’s look at the major differences again:
Availability
-
ElementRef
and@Attribute()
makes attribute’s value available inconstructor
through dependency injection -
@Input()
makes the value available after component/directive initialization, i.e. in thengOnInit
life-cycle hook.
Type support
-
ElementRef
and@Attribute()
supports onlystring
-
@Input()
supports all types
Value updates
-
ElementRef
and@Attribute()
don’t track value update -
@Input()
tracks value updates if property binding or text interpolation is used
General usage
- As
ElementRef
and@Attribute()
supports onlystring
type, they are generally used to read static HTML attribute’s value - As
@Input()
supports all types and it also tracks updates, it is generally used to read DOM property or custom data passed to the component/directive
Conclusion
We learned how to read the HTML attribute value in a component or directive using the ElementRef
class, @Attribute()
and @Input()
decorators. We also learned their usages, how they’re used in some built-in directives and limitations.
I have created a stackblitz for all of the code above.
Top comments (1)
Thanks, must try sthing