Hey devs! 👋
During a recent discussion with some colleagues about TypeScript decorators, we ended up deep-diving into Angular.dev and stumbled upon something interesting — the @Attribute
decorator.
Though it's not as commonly used as @Input
, it's a powerful and underutilized feature in Angular. So, I wanted to take a moment to share what I learned about it, along with a real-world example. 🚀
🤔 What is @Attribute
in Angular?
The @Attribute
decorator is a parameter decorator used in a directive's constructor to inject a host element's attribute value as a static string.
It allows a class (typically a directive or component) to access a static HTML attribute value at construction time, even if it’s not bound to an Angular input.
TL;DR:
- ✅ Great for static values from HTML attributes
- 🚫 Not reactive (doesn’t update on change)
- ✅ Accessed during instantiation, unlike
@Input
💡 Real-World Example: FontType Directive
Let’s look at a custom directive that changes text color based on a font
attribute using @Attribute
.
font-type.directive.ts
import {
Attribute,
Directive,
ElementRef,
inject,
OnInit,
Renderer2,
} from "@angular/core";
@Directive({
selector: "[fontType]",
})
export class FontType implements OnInit {
private render: Renderer2 = inject(Renderer2);
private el: ElementRef = inject(ElementRef);
constructor(
@Attribute("font") public font: "underline" | "italic" | "bold" | null
) {
console.log("Constructor Font Value:", font);
}
ngOnInit(): void {
switch (this.font) {
case "italic":
this.render.setStyle(this.el.nativeElement, "color", "red");
break;
case "bold":
this.render.setStyle(this.el.nativeElement, "color", "green");
break;
case "underline":
this.render.setStyle(this.el.nativeElement, "color", "orange");
break;
default:
console.warn("Font attribute not provided.");
}
}
}
🧪 Using It in a Component
@Component({
selector: "app-attribute",
imports: [FontType],
template: `
<h1><u>About Me</u></h1>
<p>
I’m a <b>frontend developer</b> and
<b fontType font="bold">freelancer</b> passionate about building
beautiful, responsive, and user-friendly web interfaces.
</p>
<p>
I specialize in <b><u fontType font="underline">HTML</u></b
>, <b><u fontType font="underline">CSS</u></b
>, <b><u fontType font="underline">JavaScript</u></b
>, and modern frameworks like <b>React</b> and <b>Vue</b>.
</p>
<p>
<i fontType font="italic"
>I believe that great design is just as important as great code</i
>, and I’m always exploring new technologies to enhance user experience.
</p>
<p>
Whether it’s a fresh build or revamping a legacy project,
<b
><i><u>let’s create something amazing together!</u></i></b
>
</p>
`,
styleUrls: ["./attribute.scss"],
})
export class AboutComponent {
constructor() {
console.log("About component initialized");
}
}
🖍️ Here, the directive is applied on <b>
, <u>
, and <i>
tags, and the color is determined by the font
attribute passed in HTML.
Output :
⚔️ @Input()
vs @Attribute()
While both seem similar at first glance, there are key differences:
Feature | @Input() |
@Attribute() |
---|---|---|
When it's read | After component init | During constructor execution |
Reactive | ✅ Yes | ❌ No |
Source | Angular bindings | Static HTML attribute |
Best used for | Dynamic values | Static metadata |
So, use @Attribute()
when you want a one-time read of a static attribute, and @Input()
when you want reactivity.
🔧 Under the Hood: Ivy Compilation
Angular compilers process the directive into ivy command as follow :
var FontType = class _FontType {
font;
render = inject3(Renderer2);
el = inject3(ElementRef);
constructor(font) {
this.font = font;
console.log(font);
}
ngOnInit() {
switch (this.font) {
case "italic":
{
this.render.setStyle(this.el.nativeElement, "color", "red");
break;
}
;
case "bold":
{
this.render.setStyle(this.el.nativeElement, "color", "green");
break;
}
;
case "underline":
{
this.render.setStyle(this.el.nativeElement, "color", "orange");
break;
}
;
default:
{
console.log("font not provided");
}
}
}
static \u0275fac = function FontType_Factory(__ngFactoryType__) {
return new (__ngFactoryType__ || _FontType)(i04.\u0275\u0275injectAttribute("font"));
}
;
static \u0275dir = /* @__PURE__ */
i04.\u0275\u0275defineDirective({
type: _FontType,
selectors: [["", "fontType", ""]]
});
}
;
( () => {
(typeof ngDevMode === "undefined" || ngDevMode) && i04.\u0275setClassMetadata(FontType, [{
type: Directive,
args: [{
selector: "[fontType]"
}]
}], () => [{
type: void 0,
decorators: [{
type: Attribute,
args: ["font"]
}]
}], null);
}
)();
Here’s a quick peek at what Angular does behind the scenes when using @Attribute
:
static ɵfac = function FontType_Factory(t) {
return new (t || FontType)(ɵɵinjectAttribute('font'));
}
This shows that Angular injects the value during directive instantiation.
🚫 When Not to Use @Attribute
- If the attribute is bound dynamically via Angular (
[attr.font]
) — use@Input()
instead. - If you need to track changes to the attribute over time.
🧪 Bonus Test: Changing Attribute Dynamically
Let’s see what happens when we try to change the attribute after the view is initialized:
ngAfterViewInit(): void {
console.log('Before changing font attribute:', this.font);
this.render.setAttribute(this.el.nativeElement, 'font', 'some_random_value');
console.warn('After changing font attribute:', this.font);
}
🔍 Result: The font
value remains the same because @Attribute()
only reads the value once during construction — no updates afterward!
✅ Wrap Up
The @Attribute
decorator is perfect for grabbing static, non-dynamic metadata from the DOM at construction time. While it's not as flexible as @Input
, it serves a specific purpose in Angular's ecosystem.
So next time you’re building a directive or component and just need a static HTML attribute — give @Attribute()
a try! 🎯
💬 Have you used @Attribute
in your projects? Got a cool use case or questions? Let’s chat in the comments below!
✍️ Author: Vetriselvan
👨💻 Frontend Developer | Code Lover | Exploring Angular’s future
Top comments (2)
This was such a well-written and thoughtful post—I really enjoyed the way you explained your ideas and offered a fresh perspective. Looking forward to reading more from you!
Really appreciate the feedback. Always happy to learn and improve. Feel free to share more anytime