DEV Community

Cover image for 🔥 Exploring `@HostBinding()` in Angular -- Decorator series -- 3
vetriselvan Panneerselvam
vetriselvan Panneerselvam

Posted on

🔥 Exploring `@HostBinding()` in Angular -- Decorator series -- 3

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

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

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() { 
  }

Enter fullscreen mode Exit fullscreen mode

🎨 SCSS Snippet

.invalid {
  border: 1px solid red !important;
}
.pending {
  border: 1px solid orange !important;
}
Enter fullscreen mode Exit fullscreen mode

🧠 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 = '';
}

Enter fullscreen mode Exit fullscreen mode

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

Which eventually invokes:

setProperty(el: RElement, name: string, value: any): void {
  (el as any)[name] = value;
}
Enter fullscreen mode Exit fullscreen mode

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

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

🚀 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)

Collapse
 
adaikkalapitchai_arumugam profile image
ADAIKKALAPITCHAI ARUMUGAM

your methods and teaching skills are very unique.
apart from your explanation your examples are very understandable...

keep it well vetri

Collapse
 
vetriselvan_11 profile image
vetriselvan Panneerselvam

Thanks for your feedback @adaikkalapitchai_arumugam