Skip to main content

Overview

The @Directive decorator marks a class as an Angular directive and provides configuration metadata. Directives attach custom behavior to elements in the DOM.

Signature

@Directive(metadata: Directive): TypeDecorator

Parameters

metadata
Directive
required
Configuration object that specifies how the directive should be processed and used.

Directive Metadata

selector

selector
string
CSS selector that identifies elements to which this directive is applied.
selector: '[appHighlight]'    // Attribute selector
selector: '.highlight'        // Class selector
selector: 'button[type=submit]' // Combined selector

standalone

standalone
boolean
default:"true"
When true, the directive does not need to be declared in an NgModule.
standalone: true  // Recommended for new directives

inputs

inputs
string[] | {name: string, alias?: string, required?: boolean, transform?: Function}[]
Input properties that accept data binding.
inputs: ['color', {name: 'highlightColor', alias: 'highlight'}]

outputs

outputs
string[]
Output properties that emit events.
outputs: ['colorChanged']

providers

providers
Provider[]
Services available for dependency injection in this directive.
providers: [DirectiveService]

exportAs

exportAs
string
Name for template variable references.
exportAs: 'highlight'
Usage: <div appHighlight #h="highlight"></div>

host

host
{[key: string]: string}
Map of host element bindings, properties, attributes, and events.
host: {
  '[style.backgroundColor]': 'backgroundColor',
  '(mouseenter)': 'onMouseEnter()',
  '(mouseleave)': 'onMouseLeave()',
  'role': 'button'
}

hostDirectives

hostDirectives
Type<unknown>[] | {directive: Type<unknown>, inputs?: string[], outputs?: string[]}[]
Directives to apply to the host element, enabling directive composition.
hostDirectives: [
  TooltipDirective,
  {directive: DisabledDirective, inputs: ['appDisabled: disabled']}
]

Attribute Directive Example

highlight.directive.ts
import { Directive, ElementRef, Input, HostListener } from '@angular/core';

@Directive({
  selector: '[appHighlight]',
  standalone: true
})
export class HighlightDirective {
  @Input() appHighlight = 'yellow';
  @Input() defaultColor = 'transparent';

  constructor(private el: ElementRef) {}

  @HostListener('mouseenter') onMouseEnter() {
    this.highlight(this.appHighlight);
  }

  @HostListener('mouseleave') onMouseLeave() {
    this.highlight(this.defaultColor);
  }

  private highlight(color: string) {
    this.el.nativeElement.style.backgroundColor = color;
  }
}
Usage:
<p appHighlight="yellow">Hover over me!</p>
<p [appHighlight]="color" defaultColor="lightblue">Custom colors</p>

Structural Directive Example

unless.directive.ts
import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';

@Directive({
  selector: '[appUnless]',
  standalone: true
})
export class UnlessDirective {
  private hasView = false;

  constructor(
    private templateRef: TemplateRef<any>,
    private viewContainer: ViewContainerRef
  ) {}

  @Input() set appUnless(condition: boolean) {
    if (!condition && !this.hasView) {
      this.viewContainer.createEmbeddedView(this.templateRef);
      this.hasView = true;
    } else if (condition && this.hasView) {
      this.viewContainer.clear();
      this.hasView = false;
    }
  }
}
Usage:
<p *appUnless="condition">Show this when condition is false</p>

Host Bindings Example

button-role.directive.ts
import { Directive, HostBinding, HostListener, Input } from '@angular/core';

@Directive({
  selector: '[appButtonRole]',
  standalone: true,
  host: {
    'role': 'button',
    '[attr.aria-pressed]': 'pressed',
    '[class.pressed]': 'pressed'
  }
})
export class ButtonRoleDirective {
  @Input() pressed = false;

  @HostListener('click')
  toggle() {
    this.pressed = !this.pressed;
  }
}

Directive Composition

menu-item.directive.ts
import { Directive } from '@angular/core';
import { TooltipDirective } from './tooltip.directive';
import { DisabledDirective } from './disabled.directive';

@Directive({
  selector: '[appMenuItem]',
  standalone: true,
  hostDirectives: [
    {
      directive: TooltipDirective,
      inputs: ['tooltipText: tooltip']
    },
    {
      directive: DisabledDirective,
      inputs: ['isDisabled: disabled']
    }
  ]
})
export class MenuItemDirective {
  // Inherits tooltip and disabled functionality
}
Usage:
<button appMenuItem tooltip="Save changes" [disabled]="!isValid">
  Save
</button>

Input Transformation

auto-id.directive.ts
import { Directive, Input } from '@angular/core';

@Directive({
  selector: '[appAutoId]',
  standalone: true,
  host: {
    '[id]': 'id'
  }
})
export class AutoIdDirective {
  private static nextId = 0;

  @Input({ transform: (value: string | number) => 
    value ? String(value) : `auto-id-${AutoIdDirective.nextId++}`
  })
  id: string = '';
}

Querying Directive Instances

parent.component.ts
import { Component, ViewChildren, QueryList, AfterViewInit } from '@angular/core';
import { HighlightDirective } from './highlight.directive';

@Component({
  selector: 'app-parent',
  standalone: true,
  imports: [HighlightDirective],
  template: `
    <p appHighlight="red">First</p>
    <p appHighlight="blue">Second</p>
    <p appHighlight="green">Third</p>
  `
})
export class ParentComponent implements AfterViewInit {
  @ViewChildren(HighlightDirective) highlights!: QueryList<HighlightDirective>;

  ngAfterViewInit() {
    console.log(`Found ${this.highlights.length} highlight directives`);
  }
}

Export As Example

form-control.directive.ts
import { Directive, Input } from '@angular/core';

@Directive({
  selector: '[appFormControl]',
  standalone: true,
  exportAs: 'formControl'
})
export class FormControlDirective {
  @Input() value: any;
  
  valid = true;
  touched = false;
  
  markAsTouched() {
    this.touched = true;
  }
  
  validate() {
    this.valid = !!this.value;
    return this.valid;
  }
}
Usage:
<input appFormControl #ctrl="formControl" [(ngModel)]="name">
<button (click)="ctrl.validate()">Validate</button>
<div *ngIf="!ctrl.valid && ctrl.touched">Field is required</div>

Best Practices

  • Keep directives focused on a single responsibility
  • Use descriptive selector names with prefixes (e.g., appHighlight)
  • Prefer host metadata over @HostBinding and @HostListener decorators
  • Clean up subscriptions in ngOnDestroy
  • Use structural directives to conditionally add/remove DOM elements
  • Use attribute directives to change appearance or behavior
  • Leverage directive composition with hostDirectives to reuse functionality

See Also

Directives Guide

Learn directive fundamentals

Attribute Directives

Create attribute directives

Structural Directives

Build structural directives

Directive Composition

Compose directive behaviors