Attribute directives
A directive adds behavior to an ordinary element. A component is a directive with a template, while an attribute directive has no template: you write it as an attribute on any tag, and it changes the look or behavior of that tag. For example, highlighting a row on mouse hover.
Creating a directive
ng generate directive highlightThe CLI creates the file src/app/highlight.ts with the Highlight class (before version 20 — highlight.directive.ts and HighlightDirective):
// src/app/highlight.ts
import { Directive } from '@angular/core';
@Directive({
selector: '[appHighlight]',
host: {
'[style.backgroundColor]': "'lightyellow'",
},
})
export class Highlight {}A selector in square brackets means "an element with the appHighlight attribute". The host property sets bindings on that element itself — here, the background.
Applying it
Like a component, a directive is added to imports and written as an attribute:
// src/app/todo-list/todo-list.ts
import { Component, inject } from '@angular/core';
import { Highlight } from '../highlight';
import { TodoStore } from '../todo-store';
@Component({
selector: 'app-todo-list',
imports: [Highlight],
templateUrl: './todo-list.html',
})
export class TodoList {
store = inject(TodoStore);
}<!-- src/app/todo-list/todo-list.html -->
<ul>
@for (todo of store.todos(); track todo.id) {
<li appHighlight>{{ todo.title }}</li>
}
</ul>All list items get a light yellow background.
Reacting to events
In host you can listen to the element's events. Let's highlight only on hover and keep the state in a signal:
// src/app/highlight.ts
import { Directive, signal } from '@angular/core';
@Directive({
selector: '[appHighlight]',
host: {
'(mouseenter)': 'active.set(true)',
'(mouseleave)': 'active.set(false)',
'[style.backgroundColor]': "active() ? 'lightyellow' : ''",
},
})
export class Highlight {
active = signal(false);
}Hover over a task — the row is highlighted; move away — the background disappears.
A directive parameter
It is convenient to pass the color through the attribute itself. To do that, give an input an alias that matches the selector:
// src/app/highlight.ts
import { Directive, input, signal } from '@angular/core';
@Directive({
selector: '[appHighlight]',
host: {
'(mouseenter)': 'active.set(true)',
'(mouseleave)': 'active.set(false)',
'[style.backgroundColor]': "active() ? (color() || 'lightyellow') : ''",
},
})
export class Highlight {
color = input('', { alias: 'appHighlight' });
active = signal(false);
}<!-- src/app/todo-list/todo-list.html -->
<li appHighlight="lightblue">{{ todo.title }}</li>
<li [appHighlight]="todo.done ? 'lightgreen' : 'pink'">{{ todo.title }}</li>Without an attribute value, yellow is used. In the second line completed tasks are highlighted green, the rest pink.
Built-in attribute directives
Angular has ready-made NgClass and NgStyle for sets of classes and styles. Today people usually write [class.name] and [style.property] bindings or [class] with an object instead — see the styling lesson.
Common mistake
Changing the element directly through ElementRef and nativeElement.style. It works, but bypasses Angular: such code is harder to test and does not work with server-side rendering. For styles, classes and events use the host property.