Directive Composition API (v15)
📚 What is Directive Composition API?
hostDirectives lets you compose directives together, applying multiple behaviors:
- Code reuse: Combine existing directive behaviors
- No inheritance: Composition over inheritance
- Expose inputs/outputs: Selectively expose directive APIs
- Works on components: Components can have host directives too
🎯 Interview Questions
- Q1: What is the Directive Composition API?
- A: Feature that allows applying multiple directives to a host element using hostDirectives property, enabling composition without inheritance.
- Q2: How do you expose host directive inputs/outputs?
- A: In hostDirectives array, specify inputs: ['inputName'] and outputs: ['outputName'] to expose them on the host.
- Q3: Can you use host directives on components?
- A: Yes! Components can use hostDirectives just like directives, allowing behavior composition.
- Q4: What's the execution order of host directives?
- A: Host directives are instantiated before the host class, in the order specified in the array.
🔧 Live Demo - Individual Directives
👆 Hover me - I have appHighlightable directive (light blue)
👆 Hover & Click me - I have BOTH directives applied manually
🔧 Live Demo - Composed Directive
👆 Hover & Click me - I use appInteractiveCard which COMPOSES both behaviors!
👆 Another card with different highlight color
The appInteractiveCard directive uses hostDirectives to combine HighlightableDirective and ClickableDirective!
💻 Directive Composition Code
import { Directive, Component, input } from '@angular/core';
// ===== Base directives to compose =====
@Directive({ selector: '[appHighlightable]' })
export class HighlightableDirective {
highlightColor = input('yellow');
@HostListener('mouseenter') onEnter() {
this.el.nativeElement.style.backgroundColor = this.highlightColor();
}
@HostListener('mouseleave') onLeave() {
this.el.nativeElement.style.backgroundColor = '';
}
}
@Directive({ selector: '[appClickable]' })
export class ClickableDirective {
@HostListener('click') onClick() {
// Click effect
}
}
// ===== Composed directive using hostDirectives =====
@Directive({
selector: '[appInteractiveCard]',
hostDirectives: [
{
directive: HighlightableDirective,
inputs: ['highlightColor'], // Expose this input
},
ClickableDirective, // No need to expose anything
],
})
export class InteractiveCardDirective {
// All the behaviors are composed automatically!
}
// ===== Using on a component =====
@Component({
selector: 'app-button',
hostDirectives: [
{
directive: TooltipDirective,
inputs: ['appTooltip: tooltip'], // Alias: appTooltip -> tooltip
},
{
directive: DisabledDirective,
inputs: ['disabled'],
outputs: ['disabledChange'],
},
],
template: `<ng-content />`,
})
export class ButtonComponent {
// Component now has tooltip and disabled behaviors!
}
// Usage:
// <app-button tooltip="Click me!" [disabled]="false">
// Save
// </app-button>