Directives
About
They are classes that add additional behavior to elements in your applications.
Use Angular's built-in directives to manage forms, lists, styles and what users see.
ng g directive [directive-name] [options]
Best Practices
Avoid changing elements directly through the DOM like
*.nativeElement.style.*
, since angular is able to render the Templates without a DOM, for instance when using service workers, then these properties may not be available.
Attribute Directives
Change the appearance or behavior of an element, component, or another directive.
They listen to and modify the behavior of other HTML elements, attributes, properties, and components.
Built-in Attribute Directives
NgClass
NgClass
An old way of adding and removing CSS classes in elements.
Preffer to use Class Binding [class.].
@Component({
template: `
<!-- Adding 'special' class based on 'isSpecial' value -->
<div [ngClass]="isSpecial ? 'special' : ''">This div is special</div>
<!-- Adding multiple classes with object 'multipleClasses', where each key is the class that will be applied based on its value -->
<div [ngClass]="multipleClass">Div of multiple classes</div>
`
})
export class Component {
protected isSpecial = true;
protected multipleClasses = {
'className1': true,
'className2': false,
}
}
NgStyle
NgStyle
An old way of adding and removing HTML styles in elements.
Preffer to use Style Binding [style.].
@Component({
template: `
<!-- Adding single inline style -->
<div [ngStyle]="'font-size': true ? '10px' : '20px'">This div is special</div>
<!-- Adding multiple classes with object 'multipleClasses', where each key is the class that will be applied based on its value -->
<div [ngStyle]="multipleStyles">Div of multiple classes</div>
`
})
export class Component {
protected isSpecial = true;
// You may use regular style names, or camelCase names without quotes
protected multipleStyles = {
'font-weight': true ? 'bold': 'normal',
fontSize: false ? '10px' : '20px',
}
}
NgModel
NgModel
Use it to display a data property and update that property when the user makes changes. (Adds two-way data binding to HTML form elements)
<label for="example-ngModel">[(ngModel)]:</label>
<input [(ngModel)]="currentItem.name" id="example-ngModel">
<input [ngModel]="currentItem.name" (ngModelChange)="setUppercaseName($event)" id="example-uppercase">
More examples in Examples.
Create the new directive with the CLI command.
By default a directive will have a [appDirective]
attribute seletor, but it could be a class or component selector as well.
@Directive({
standalone: true,
selector: "[appNew]",
})
export class NewDirective {
// Inject the `ElementRef` to get the reference to the Host DOM element
constructor(private el: ElementRef) {
this.el.nativeElement.style.backgroundColor = "yellow";
}
}
<p appNew>This will have yellow background.</p>
Passing values into an attribute directive
Pass values to the Directive with input()
using the same selector name as the variable name.
export class NewDirective {
// This uses the same variable name as the directive's selector
appNew = input("yellow");
constructor(private el: ElementRef) {
this.el.nativeElement.style.backgroundColor = this.appNew;
}
}
<p [appNew]="white">This will override the color to white.</p>
Handling user events
You can handle user events or subscribe to event of the DOM
with @HostListener()
.
But prefer to use Binding to the host element.
export class NewDirective {
constructor(private el: ElementRef) {}
@HostListener("mouseenter") onMouseEnter() {
this.highlight(true);
}
@HostListener("mouseleave") onMouseLeave() {
this.highlight();
}
private highlight(state = false) {
this.el.nativeElement.style.backgroundColor = state ? "yellow" : "transparent";
}
}
Structural Directives
Change the DOM
layout by adding and removing DOM
elements.
Structural directives can be raw applied to <ng-template>
only.
<ng-template [ngIf]=""><p>Paragraph</p></ng-template>
<ng-template newStructuralDirective let-item [selectFrom]="items">
<p>Paragraph</p>
</ng-template>
Angular expanded the use of them to other HTML elements by the use of shorthand syntax (*
).
<p *ngIf="">Paragraph</p>
<p *newStructuralDirective="let item from items">Paragraph</p>
You can only apply one structural directive per element when using the shorthand syntax (*
).
If multiple directives need to be applied, apply them in a <ng-container>
.
Built-in Structural Directives
Conditionally creates or disposes of subviews from the template. (Frees up memory and resources)
By default, ngIf
prevents display of an element bound to a null value.
<div *ngIf="isActive">Div</div>
To use an else
block, must use a <ng-template>
block with the help of a LocalReference
.
<div *ngIf="isActive; else elseBlock">Div</div>
<ng-template #elseBlock>Div otherwise</ng-template>
Or with a more descriptive way.
<ng-template [ngIf]="isActive" [ngIfElse]="elseBlock">
<div>Div</div>
</ng-template>
<ng-template #elseBlock>
<div>Div otherwise</div>
</ng-temaplate>
NgFor
NgFor
Repeat a node for each item in a list.
@Component({
template: `
<div *ngFor="let item of items">
<span>{{ item.value }}</span>
</div>
`,
})
export class Component {
protected items = [{ value: "Value1" }, { value: "Value2" }];
}
You may pass the item
value to a sub component like.
<app-sub-component *ngFor="let item of items" [itemData]="item" />
You can get the list index
with let i=index
like.
<div *ngFor="let item of items; let i=index">
<span [number]="i">{{ item.value }}</span>
</div>
You can track items with trackBy
.
Angular can change and re-render only those items that have changed, rather than reloading the entire list of items.
The item list must have some sort of unique id
field.
@Component({
template: `
<div *ngFor="let item of items; trackBy: item.id">
<span>{{ item.value }}</span>
</div>
`,
})
export class Component {
protected items = [
{ id: 1, value: "Value1" },
{ id: 2, value: "Value2" },
];
}
NgSwitch
NgSwitch
To display one element among several possible elements, based on a switch condition.
@Component({
template: `
<ng-template [ngFor]="let item of items; trackBy: item.id">
<div [ngSwitch]="item.value">
<app-sub-component *ngSwitchCase="'Value1'" [itemData]="item" />
<app-sub-component *ngSwitchCase="'Value2'" [itemData]="item" />
<span *ngSwitchDefault >Unknown</span>
</div
</ng-template>
`,
})
export class Component {
protected items = [
{ id: 1, value: "Value1" },
{ id: 2, value: "Value2" },
];
}
Create the new directive with the CLI command.
@Directive({
selector: "[select]",
})
export class SelectDirective {
private templateRef = inject(TemplateRef);
private viewContainerRef = inject(ViewContainerRef);
selectFrom = input.required<DataSource>();
async ngOnInit() {
const data = await this.selectFrom.load();
this.viewContainerRef.createEmbeddedView(this.templateRef, {
/*
Create the embedded view with a context object that contains
the data via the key `$implicit`.
*/
$implicit: data,
});
}
}
<ng-template selector let-item [selectFrom]="items">
<p>The data is: {{ item.value }}</p>
</ng-template>
Adding Directives to a Component with hostDirectives
hostDirectives
Only usable in standalone
components.
You can assign directives to a component through the hostDirectives
property inside the component's decorator.
@Component({
hostDirectives: [MenuBehavior],
})
Angular applies host directives statically at compile time.
You cannot dynamically add directives at runtime.
Angular ignores the selector
of directives applied in the hostDirectives
property.
Execution order
Host directives always execute their constructor, lifecycle hooks, and binding before the component or directive on which they are applied.
This means that components with hostDirectives
can override any host binding specified by a host directive.
Including inputs and outputs
By explicitly specifying the inputs and outputs, consumers of the component with hostDirective
can bind them in a template.
@Component({
hostDirectives: [{
directive: MenuBehavior,
inputs: ['menuId'],
outputs: ['menuClosed']
}],
})
export class AdminMenu { ... }
<admin-menu menuId="top-menu" (menuClosed)="logMenuClosed()">
Alias the inputs and outputs
You can also alias them to customize the API of your component.
@Component({
hostDirectives: [{
directive: MenuBehavior,
inputs: ['menuId: id'],
outputs: ['menuClosed: closed']
}],
})
export class AdminMenu { ... }
<admin-menu id="top-menu" (closed)="logMenuClosed()">
Adding Directives to other Directives with hostDirectives
hostDirectives
You can also add hostDirectives
to other directives, in addition to components.
This enables the transitive aggregation of multiple behaviors (chainned directives).
@Directive({ ... })
export class Menu { ... }
@Directive({ ... })
export class Tooltip { ... }
// MenuWithTooltip can compose behaviors from multiple other directives
@Directive({
hostDirectives: [Tooltip, Menu],
})
export class MenuWithTooltip { ... }
// CustomWidget can apply the already-composed behaviors from MenuWithTooltip
@Directive({
hostDirectives: [MenuWithTooltip],
})
export class SpecializedMenuWithTooltip { }
Last updated