Skip to the solution
MediumFrontend9 min read

What do @HostListener and @HostBinding do?

691 views

The problem

Question presented to candidate: "Build a toggle directive that adds a class and an aria attribute to its element when clicked and resets on Escape anywhere on the page. Show it with @HostListener and @HostBinding, then tell me what the Angular docs recommend instead today."

What a strong answer should cover:

  • Host element: the element a directive or component is attached to; host bindings change it, host listeners listen on it.
  • @HostBinding('class.on'), 'attr.aria-pressed', 'style.width.px', plain properties; @HostListener('click', ['$event']), global targets document: / window:, key filters like keydown.escape.
  • A @HostListener handler that returns false calls preventDefault(); a null attribute binding removes the attribute.
  • The 22.2.3 typings say to always prefer the host property in @Component / @Directive metadata; the decorators exist for backwards compatibility.
  • Both forms compile to the same host-binding instructions, and host expressions are type-checked with strict templates.
  • With signals in the host bindings, OnPush and zoneless are handled naturally.

Clarifying questions expected:

  • "Should Escape be listened for on the document, or only while the element has focus?"
  • "Is this a directive on existing elements, or a component styling its own host?"

Code / implementation expected: Yes — the same directive written once with decorators and once with host metadata.

  • hostlistener
  • hostbinding

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

9 min read · 9 sections

Read the code

AngularThe same toggle directive with @HostBinding / @HostListener and with host metadata
import { Component, Directive, HostBinding, HostListener, signal } from "@angular/core";

// Old style: decorators on class members.
@Directive({ selector: "[toggleOld]" })
export class ToggleOldDirective {
  @HostBinding("class.on") on = false;
  @HostBinding("attr.aria-pressed") get pressed() { return String(this.on); }
  @HostBinding("style.width.px") width = 120;
  @HostListener("click", ["$event.type"])
  toggle(type: string) {
    this.on = !this.on;
    console.log("old directive got", type);
  }
  @HostListener("document:keydown.escape")
  reset() { this.on = false; }
}

// Recommended style: the host property in the metadata.
@Directive({
  selector: "[toggleNew]",
  host: {
    role: "button",                          // static attribute
    "[class.on]": "on()",
    "[attr.aria-pressed]": "on()",
    "[style.width.px]": "120",
    "(click)": "toggle($event)",
    "(document:keydown.escape)": "on.set(false)",
  },
})
export class ToggleNewDirective {
  on = signal(false);
  toggle(e: MouseEvent) {
    this.on.update((v) => !v);
    console.log("new directive got", e.type);
  }
}

@Component({
  selector: "app-root",
  imports: [ToggleOldDirective, ToggleNewDirective],
  styles: `span { display: inline-block; padding: 4px; border: 1px solid #888; cursor: pointer; }
    .on { background: gold; }`,
  template: `<span id="old" toggleOld>old</span> <span id="new" toggleNew>new</span>
    <p>Click a box to toggle it, press Escape to reset both.</p>`,
})
export class AppComponent {}

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 40 of 121 in Angular

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

All Angular questions
Up nextMedium

How do you optimize initial bundle size using Route Preloading with custom strategies?

Continue