Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

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 highlight

The 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.

Thanks for reading!

Donate to the site — we work hard for you.