Lesson 11 of 30
A directive is a component without a template — it attaches behavior to an element instead of rendering one. Host bindings and host listeners are how it talks to the DOM.
14 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
This topic is about judgment and practical tradeoffs more than a single right answer — a mock interview is the better way to test yourself on it.
Be interviewed on this →Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
A component is really just a directive with a template attached. Strip the template away and you're left with the more general concept: a class that Angular instantiates and attaches to a DOM element to give it extra behavior, without that element belonging to the directive itself. That's an attribute directive — something you drop onto an existing element, like <button appHighlight>.
There's a second kind, a structural directive, which doesn't just decorate an element — it controls whether and how many times a chunk of template gets stitched into the DOM at all. *ngIf and *ngFor used to be the go-to structural directives; today @if and @for are compiler-native template blocks that do the same job without you writing a directive class at all, so you'll rarely need to write a structural directive yourself. Attribute directives, on the other hand, are still exactly what you reach for whenever you want to attach reusable DOM behavior — a tooltip, an autofocus, a permission-based visibility toggle — to any element without wrapping it in a new component.
The mechanism a directive uses to actually touch the DOM is the host — the element it's attached to. You read from it and write to it through host bindings (setting a property or attribute on the host element) and host listeners (reacting to an event the host element fires).
import { Directive, input } from '@angular/core';
@Directive({
selector: '[appHighlight]',
standalone: true,
host: {
'[style.backgroundColor]': 'backgroundColor()',
'(mouseenter)': 'onMouseEnter()',
'(mouseleave)': 'onMouseLeave()',
},
})
export class HighlightDirective {
appHighlight = input<string>('#fef08a'); // the [appHighlight]="'...'" binding itself
private hovering = false;
backgroundColor(): string | null {
return this.hovering ? this.appHighlight() : null;
}
onMouseEnter(): void {
this.hovering = true;
}
onMouseLeave(): void {
this.hovering = false;
}
}
Used as <div appHighlight="#bae6fd">Hover me</div> or just <div appHighlight> to take the default color. Everything lives in the host metadata object: the keys in square brackets ('[style.backgroundColor]') are host bindings — they set a property, attribute, class, or style on the host element, re-evaluated whenever the expression's dependencies change. The keys in parentheses ('(mouseenter)') are host listeners — they wire up a DOM event on the host element to a method on the directive.
Notice the directive never touches ElementRef or does manual nativeElement.style.backgroundColor = ... — the host binding does that declaratively, the same way a property binding in a component template would. That's deliberate: reaching for direct DOM manipulation defeats the point of letting Angular track and batch the updates for you.
[appHighlight], not an element tag. A component selector like app-product-card matches a whole element; an attribute directive's selector matches an attribute name on any element, which is exactly what lets you drop the same directive onto a <div>, a <button>, or an <a> interchangeably.*ngIf / *ngFor custom structural-directive knowledge anymore. Writing a structural directive requires understanding TemplateRef and ViewContainerRef directly — real, but niche today, since @if/@for/@switch cover the vast majority of conditional-and-loop template logic as compiler-native blocks, not directives you'd write yourself.<button appHighlight appTooltip="Save"> runs both directives' host bindings and listeners independently against the same host — directives compose by stacking, unlike a component which is exclusively one class per element.A custom attribute directive is Angular's answer to the same problem React solves with a custom hook that returns a ref and some DOM-facing handlers — both let you package up imperative-feeling behavior (hover state, focus management, measurement) as something reusable, without forcing the caller to accept an extra wrapping element just to attach it.