Creating Attribute Directives
Attribute directives change the appearance or behavior of an element without altering the DOM structure. They are applied as attributes on existing elements.
Basic Directive
import { Directive, ElementRef, Renderer2 } from '@angular/core';
@Directive({
selector: '[appHighlight]',
standalone: true,
})
export class HighlightDirective {
constructor(private el: ElementRef, private renderer: Renderer2) {
this.renderer.setStyle(this.el.nativeElement, 'background-color', 'yellow');
}
}
<p appHighlight>This text has a yellow background</p>
Using @HostListener
@HostListener listens to events on the host element:
import { Directive, HostListener } from '@angular/core';
@Directive({
selector: '[appTooltip]',
standalone: true,
})
export class TooltipDirective {
@HostListener('mouseenter')
onMouseEnter(): void {
console.log('Mouse entered');
}
@HostListener('mouseleave')
onMouseLeave(): void {
console.log('Mouse left');
}
@HostListener('click', ['$event'])
onClick(event: MouseEvent): void {
console.log('Clicked at:', event.clientX, event.clientY);
}
}
<button appTooltip>Hover over me</button>
Using @HostBinding
@HostBinding sets properties on the host element:
import { Directive, HostBinding, Input } from '@angular/core';
@Directive({
selector: '[appHighlight]',
standalone: true,
})
export class HighlightDirective {
@Input() appHighlight = '';
@HostBinding('style.backgroundColor')
get backgroundColor(): string {
return this.appHighlight || 'yellow';
}
@HostBinding('class.highlighted')
get isHighlighted(): boolean {
return !!this.appHighlight;
}
}
<p appHighlight="lightblue">Blue highlight</p>
<p appHighlight>Default yellow highlight</p>
Advanced Directive Patterns
Directives can accept multiple inputs, inject services, and compose with other directives.
Multiple Inputs
import { Directive, Input, HostListener, HostBinding } from '@angular/core';
@Directive({
selector: '[appResize]',
standalone: true,
})
export class ResizeDirective {
@Input() appResize: 'small' | 'medium' | 'large' = 'medium';
@Input() resizable = true;
@HostBinding('class.resize-small')
get isSmall(): boolean { return this.appResize === 'small'; }
@HostBinding('class.resize-medium')
get isMedium(): boolean { return this.appResize === 'medium'; }
@HostBinding('class.resize-large')
get isLarge(): boolean { return this.appResize === 'large'; }
@HostBinding('style.cursor')
get cursor(): string { return this.resizable ? 'resize' : 'default'; }
}
<div appResize="large" [resizable]="true">Resizable content</div>
Injecting Services in Directives
import { Directive, inject } from '@angular/core';
import { NotificationService } from './notification.service';
@Directive({
selector: '[appConfirm]',
standalone: true,
})
export class ConfirmDirective {
private notification = inject(NotificationService);
@HostListener('click')
onClick(): void {
const confirmed = confirm('Are you sure?');
if (confirmed) {
this.notification.show('Action confirmed');
}
}
}
Directive with Renderer2 (Safe DOM Manipulation)
Always use Renderer2 instead of direct DOM access for server-side rendering compatibility:
import { Directive, ElementRef, Renderer2, inject } from '@angular/core';
@Directive({
selector: '[appLoading]',
standalone: true,
})
export class LoadingDirective {
private el = inject(ElementRef);
private renderer = inject(Renderer2);
@Input() set appLoading(isLoading: boolean) {
if (isLoading) {
this.renderer.addClass(this.el.nativeElement, 'loading');
this.renderer.setAttribute(this.el.nativeElement, 'aria-busy', 'true');
} else {
this.renderer.removeClass(this.el.nativeElement, 'loading');
this.renderer.removeAttribute(this.el.nativeElement, 'aria-busy');
}
}
}
<div [appLoading]="isLoading">Content</div>
Quiz
1. What is the difference between a structural directive and an attribute directive?
2. How do you listen to events on the host element in a directive?
3. Why should you use Renderer2 instead of direct DOM manipulation?
Flashcards
Question
What is an attribute directive?
Click to reveal answer
Answer
A directive that changes the appearance or behavior of an element without altering the DOM structure.
Question
How do you listen to host element events?
Click to reveal answer
Answer
@HostListener('click') — decorator that binds to DOM events on the host element.
Question
Why use Renderer2?
Click to reveal answer
Answer
Provides DOM abstraction that works with SSR and web workers. Never access nativeElement directly.
Revision Notes
Key Takeaways
- 1. Attribute directives change appearance/behavior without altering DOM structure
- 2. @HostListener responds to events on the host element
- 3. @HostBinding sets properties on the host element
- 4. Use Renderer2 for safe DOM manipulation (SSR-compatible)
Interview Tips
- • Explain the difference between structural and attribute directives
- • Be ready to describe @HostListener and @HostBinding
- • Mention Renderer2 as the safe alternative to direct DOM access
Cheat Sheet
Custom Attribute Directives Cheat Sheet
Basic directive:
@Directive({
selector: '[appHighlight]',
standalone: true,
})
export class HighlightDirective {
@HostListener('mouseenter') onMouseEnter(): void {}
@HostBinding('style.backgroundColor') get bg(): string { return 'yellow'; }
}
Usage:
<p appHighlight>Highlighted text</p>
Key decorators:
- @HostListener — listen to host events
- @HostBinding — set host properties
- @Input — accept data from the template