Hey Devs 👋
Welcome back to the series on Angular decorators!
Today, we're diving into a powerful decorator: @HostBinding()
. We'll explore:
- ✅ What is
@HostBinding()
- 🛠️ How to use it in real-life components
- 🔍 How Angular compiles it under the hood
- 💡 Tips and lesser-known behavior
🧠 What is @HostBinding()
?
@HostBinding()
is an Angular decorator that allows you to bind properties, attributes, styles, and classes to the host element of a directive or component.
During change detection, Angular watches these bindings. If a value changes, Angular automatically updates the corresponding host element.
✨ Syntax
@HostBinding('hostPropertyName') propertyName: any;
You can bind to:
- Classes →
@HostBinding('class.className')
- Styles →
@HostBinding('style.propertyName')
- Attributes →
@HostBinding('attr.attributeName')
- DOM Properties →
@HostBinding('propertyName')
👇 Real-World Example: Login Form with Dynamic Host Bindings
Let’s build a basic login form and create a directive that dynamically applies classes (invalid
, pending
) and an id
attribute based on the input control state.
🧩 HTML Template
<div class="main-wrapper">
<div class="form-container">
<div class="logo-container">Login</div>
<form [formGroup]="formGroup" class="form">
<div class="form-group">
<label for="username">Username</label>
<input type="text" formControlName="username" placeholder="Enter your username" />
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" formControlName="password" placeholder="Enter your password" />
</div>
<button class="form-submit-btn" type="submit">Submit</button>
</form>
</div>
</div>
ts
import { CommonModule } from '@angular/common';
import { Component, inject, OnInit } from '@angular/core';
import {
AbstractControl,
AsyncValidatorFn,
FormBuilder,
FormGroup,
ReactiveFormsModule,
Validators,
} from '@angular/forms';
import { RouterLink } from '@angular/router';
import { Formfield } from '../../directive/formfield';
import { delay, map, of, timer } from 'rxjs';
@Component({
selector: 'app-login',
imports: [ReactiveFormsModule, CommonModule, RouterLink, Formfield],
templateUrl: './login.html',
styleUrl: './login.scss',
})
export class Login {
private formBuilder: FormBuilder = inject(FormBuilder);
result: string = 'start';
formGroup: FormGroup = this.formBuilder.nonNullable.group({
username: ['', [Validators.required],[this.userExistsValidator()]],
password: ['', [Validators.required]],
});
userExistsValidator():AsyncValidatorFn {
return (control: AbstractControl) => {
return of(null).pipe(delay(2000))
}
}
constructor() {
}
🎨 SCSS Snippet
.invalid {
border: 1px solid red !important;
}
.pending {
border: 1px solid orange !important;
}
🧠 Directive Behind the Magic (Simplified)
You create a directive like this:
import { Directive, HostBinding, inject, Input, OnInit } from '@angular/core';
import { NgControl } from '@angular/forms';
@Directive({
selector: '[formControlName]',
})
export class Formfield implements OnInit {
private _control: NgControl = inject(NgControl);
constructor() {}
ngOnInit(): void {}
@HostBinding('class.invalid')
get invalid() {
return this._control.touched && this._control.invalid;
}
@HostBinding('class.pending')
get pending() {
return this._control.pending;
}
@HostBinding('style.color')
get validColor() {
return this._control.touched && this._control.valid ? 'green' : 'inherit';
}
@HostBinding('id')
@Input()
formControlName: string = '';
}
In the login form example, we included a dummy asynchronous validator to simulate a delay. This was done specifically to demonstrate how the pending state works on a form control and how it can be styled using @HostBinding()
.
🔬 What Happens Under the Hood?
When compiled with Ivy, your @HostBinding()
gets translated into low-level instructions like:
ɵɵdomProperty("id", ctx.formControlName);
ɵɵstyleProp("color", ctx.validColor);
ɵɵclassProp("invalid", ctx.invalid)("pending", ctx.pending);
Which eventually invokes:
setProperty(el: RElement, name: string, value: any): void {
(el as any)[name] = value;
}
Angular is directly setting properties on the native DOM element, using its internal rendering engine.
🧩 Useful Input Element Properties for @HostBinding()
When targeting <input>
elements, here are native properties you can bind:
🧭 Behavior
Property | Description |
---|---|
disabled |
Whether the input is disabled |
readonly |
Whether the input is read-only |
required |
Whether the input is required |
autofocus |
Automatically focus the input on load |
multiple |
Allow multiple file selection (type="file") |
autocomplete |
Browser-suggested input history |
⌨️ Typing & Focus
Property | Description |
---|---|
type |
Input type (text , password , etc.) |
placeholder |
Placeholder text |
spellcheck |
Enable/disable spellcheck |
tabIndex |
Keyboard tab navigation index |
selectionStart |
Text selection start (for text inputs) |
✅ Example:
@HostBinding('attr.placeholder') placeholder = 'Enter something';
@HostBinding('disabled') isDisabled = false;
@HostBinding('type') inputType = 'text';
📌 Summary
✅ @HostBinding()
is great for dynamically binding host element properties like classes, attributes, or native input properties.
💡 Works beautifully with reactive forms to reflect validation status.
🔧 Under the hood, it compiles into Ivy host instructions like ɵɵdomProperty
, ɵɵclassProp
, and ɵɵstyleProp
.
📸 Bonus
Here's a snapshot of how Angular binds the host props:
hostBindings: function Formfield_HostBindings(rf, ctx) {
if (rf & 2) {
ɵɵdomProperty("id", ctx.formControlName);
ɵɵclassProp("invalid", ctx.invalid)("pending", ctx.pending);
}
}
🚀 That’s a Wrap!
Understanding @HostBinding()
gives you fine-grained control over your components and directives.
💬 Got questions or use cases you want to share? Drop a comment below! Let's discuss more Angular magic. ✨
✍️ Author: Vetriselvan
👨💻 Frontend Developer | Code Lover | Exploring Angular’s future
Top comments (2)
your methods and teaching skills are very unique.
apart from your explanation your examples are very understandable...
keep it well vetri
Thanks for your feedback @adaikkalapitchai_arumugam