Angular Directives
Angular Directives
- Angular directives are useful to make your angular component more interactive and make it dynamic. These directives aims to enhance the DOM manipulation for dynamic data.
In this article, we'll discuss some of the useful Angular Directives that you—as a developer—leverage on your next Angular application. Angular Directives have three different categories—component, attribute and structural directives—we’ll get into them and some of their examples.
Attribute Directives:
According to the https://angular.dev/guide/directives#built-in-attribute-directives’s documentation, Attribute Directives: The behavior of other HTML elements, attributes, properties, and components can be observed and altered using attribute directives. Let see some examples to better understand it.
NgClass- Adds and removes a set of CSS classes. So this one, think of it like having a conditional or branching happening inside your styles that based on certain conditions it'll do this otherwise, that
// Code cited from Angular Documentation:
// https://angular.dev/guide/directives#example-1
import {Component, OnInit} from '@angular/core';
import {JsonPipe} from '@angular/common';
import {NgIf} from '@angular/common';
import {NgFor} from '@angular/common';
import {NgSwitch, NgSwitchCase, NgSwitchDefault} from '@angular/common';
import {NgStyle} from '@angular/common';
import {NgClass} from '@angular/common';
import {FormsModule} from '@angular/forms';
import {Item} from './item';
import {ItemDetailComponent} from './item-detail/item-detail.component';
import {ItemSwitchComponents} from './item-switch.component';
import {StoutItemComponent} from './item-switch.component';
@Component({
standalone: true,
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
imports: [
NgIf, // <-- import into the component
NgFor, // <-- import into the component
NgStyle, // <-- import into the component
NgSwitch, // <-- import into the component
NgSwitchCase,
NgSwitchDefault,
NgClass, // <-- import into the component
FormsModule, // <--- import into the component
JsonPipe,
ItemDetailComponent,
ItemSwitchComponents,
StoutItemComponent,
],
})
export class AppComponent implements OnInit {
canSave = true;
isSpecial = true;
isUnchanged = true;
isActive = true;
nullCustomer: string | null = null;
currentCustomer = {
name: 'Laura',
};
item!: Item; // defined to demonstrate template context precedence
items: Item[] = [];
currentItem!: Item;
// trackBy change counting
itemsNoTrackByCount = 0;
itemsWithTrackByCount = 0;
itemsWithTrackByCountReset = 0;
itemIdIncrement = 1;
currentClasses: Record<string, boolean> = {};
currentStyles: Record<string, string> = {};
ngOnInit() {
this.resetItems();
this.setCurrentClasses();
this.setCurrentStyles();
this.itemsNoTrackByCount = 0;
}
setUppercaseName(name: string) {
this.currentItem.name = name.toUpperCase();
}
setCurrentClasses() {
// CSS classes: added/removed per current state of component properties
this.currentClasses = {
saveable: this.canSave,
modified: !this.isUnchanged,
special: this.isSpecial,
};
}
setCurrentStyles() {
// CSS styles: set per current state of component properties
// Here, we're changing styles based on the state, one style at a time.
this.currentStyles = {
'font-style': this.canSave ? 'italic' : 'normal',
'font-weight': !this.isUnchanged ? 'bold' : 'normal',
'font-size': this.isSpecial ? '24px' : '12px',
};
}
isActiveToggle() {
this.isActive = !this.isActive;
}
giveNullCustomerValue() {
this.nullCustomer = 'Kelly';
}
resetItems() {
this.items = Item.items.map((item) => item.clone());
this.currentItem = this.items[0];
this.item = this.currentItem;
}
resetList() {
this.resetItems();
this.itemsWithTrackByCountReset = 0;
this.itemsNoTrackByCount = ++this.itemsNoTrackByCount;
}
changeIds() {
this.items.forEach((i) => (i.id += 1 * this.itemIdIncrement));
this.itemsWithTrackByCountReset = -1;
this.itemsNoTrackByCount = ++this.itemsNoTrackByCount;
this.itemsWithTrackByCount = ++this.itemsWithTrackByCount;
}
clearTrackByCounts() {
this.resetItems();
this.itemsNoTrackByCount = 0;
this.itemsWithTrackByCount = 0;
this.itemIdIncrement = 1;
}
trackByItems(index: number, item: Item): number {
return item.id;
}
trackById(index: number, item: any): number {
return item.id;
}
getValue(event: Event): string {
return (event.target as HTMLInputElement).value;
}
}
<!-- Code snippet cited from Angular's documentation: -->
<!-- https://angular.dev/guide/directives#example-2 -->
<!-- On the element you'd like to style, add [ngClass] and set
it equal to an expression. In this case, isSpecial is a boolean
set to true in app.component.ts. Because isSpecial is true, ngClass
applies the class of special to the <div>. -->
<!-- toggle the "special" class on/off with a property -->
<div [ngClass]="isSpecial ? 'special' : ''">This div is special</div>
NgStyle: According to https://angular.dev/guide/directives#adding-and-removing-classes-with-ngclass’s Documentation UseNgStyleto set multiple inline styles simultaneously, based on the state of the component.
// Code snippets provided by Angular's documentation:
// https://angular.dev/guide/directives#example-5
import {Component, OnInit} from '@angular/core';
import {JsonPipe} from '@angular/common';
import {NgIf} from '@angular/common';
import {NgFor} from '@angular/common';
import {NgSwitch, NgSwitchCase, NgSwitchDefault} from '@angular/common';
import {NgStyle} from '@angular/common';
import {NgClass} from '@angular/common';
import {FormsModule} from '@angular/forms';
import {Item} from './item';
import {ItemDetailComponent} from './item-detail/item-detail.component';
import {ItemSwitchComponents} from './item-switch.component';
import {StoutItemComponent} from './item-switch.component';
@Component({
standalone: true,
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
// Import the necessary directive in the imports array
imports: [
NgIf, // <-- import into the component
NgFor, // <-- import into the component
NgStyle, // <-- import into the component
NgSwitch, // <-- import into the component
NgSwitchCase,
NgSwitchDefault,
NgClass, // <-- import into the component
FormsModule, // <--- import into the component
JsonPipe,
ItemDetailComponent,
ItemSwitchComponents,
StoutItemComponent,
],
})
export class AppComponent implements OnInit {
canSave = true;
isSpecial = true;
isUnchanged = true;
isActive = true;
nullCustomer: string | null = null;
currentCustomer = {
name: 'Laura',
};
item!: Item; // defined to demonstrate template context precedence
items: Item[] = [];
currentItem!: Item;
// trackBy change counting
itemsNoTrackByCount = 0;
itemsWithTrackByCount = 0;
itemsWithTrackByCountReset = 0;
itemIdIncrement = 1;
currentClasses: Record<string, boolean> = {};
currentStyles: Record<string, string> = {};
// This function will run on initialize of the component
ngOnInit() {
// Anything that put inside of this callback will be run.
this.resetItems();
this.setCurrentClasses();
this.setCurrentStyles();
this.itemsNoTrackByCount = 0;
}
setUppercaseName(name: string) {
this.currentItem.name = name.toUpperCase();
}
setCurrentClasses() {
// CSS classes: added/removed per current state of component properties
this.currentClasses = {
saveable: this.canSave,
modified: !this.isUnchanged,
special: this.isSpecial,
};
}
setCurrentStyles() {
// CSS styles: set per current state of component properties
// Here, NgClass aims to solve the limitation of the
// NgStyle since it only applies limited class if certain conditions
// is true as well as on false side.
this.currentStyles = {
'font-style': this.canSave ? 'italic' : 'normal',
'font-weight': !this.isUnchanged ? 'bold' : 'normal',
'font-size': this.isSpecial ? '24px' : '12px',
};
}
isActiveToggle() {
this.isActive = !this.isActive;
}
giveNullCustomerValue() {
this.nullCustomer = 'Kelly';
}
resetItems() {
this.items = Item.items.map((item) => item.clone());
this.currentItem = this.items[0];
this.item = this.currentItem;
}
resetList() {
this.resetItems();
this.itemsWithTrackByCountReset = 0;
this.itemsNoTrackByCount = ++this.itemsNoTrackByCount;
}
changeIds() {
this.items.forEach((i) => (i.id += 1 * this.itemIdIncrement));
this.itemsWithTrackByCountReset = -1;
this.itemsNoTrackByCount = ++this.itemsNoTrackByCount;
this.itemsWithTrackByCount = ++this.itemsWithTrackByCount;
}
clearTrackByCounts() {
this.resetItems();
this.itemsNoTrackByCount = 0;
this.itemsWithTrackByCount = 0;
this.itemIdIncrement = 1;
}
trackByItems(index: number, item: Item): number {
return item.id;
}
trackById(index: number, item: any): number {
return item.id;
}
getValue(event: Event): string {
return (event.target as HTMLInputElement).value;
}
}
<!-- Here, consuming the style based on the status. -->
<div [ngStyle]="currentStyles">
This div is initially italic, normal weight, and extra large (24px).
</div>
NgModel: According to Angular’s Documentation Use theNgModeldirective to display a data property and update that property when the user makes changes. To put it simply, one of Angular's features, the NgModel directive, helps link form inputs (such as text boxes, checkboxes, etc.) to data in your application. It enables the input to display the most recent data from the application and allows you to automatically update the data when the user modifies the input. Kind of like onChange event but with more features.
import {Component, OnInit} from '@angular/core';
import {JsonPipe} from '@angular/common';
import {NgIf} from '@angular/common';
import {NgFor} from '@angular/common';
import {NgSwitch, NgSwitchCase, NgSwitchDefault} from '@angular/common';
import {NgStyle} from '@angular/common';
import {NgClass} from '@angular/common';
import {FormsModule} from '@angular/forms';
import {Item} from './item';
import {ItemDetailComponent} from './item-detail/item-detail.component';
import {ItemSwitchComponents} from './item-switch.component';
import {StoutItemComponent} from './item-switch.component';
@Component({
standalone: true,
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
imports: [
NgIf, // <-- import into the component
NgFor, // <-- import into the component
NgStyle, // <-- import into the component
NgSwitch, // <-- import into the component
NgSwitchCase,
NgSwitchDefault,
NgClass, // <-- import into the component
FormsModule, // <--- import into the component
JsonPipe,
ItemDetailComponent,
ItemSwitchComponents,
StoutItemComponent,
],
})
export class AppComponent implements OnInit {
canSave = true;
isSpecial = true;
isUnchanged = true;
isActive = true;
nullCustomer: string | null = null;
currentCustomer = {
name: 'Laura',
};
item!: Item; // defined to demonstrate template context precedence
items: Item[] = [];
currentItem!: Item;
// trackBy change counting
itemsNoTrackByCount = 0;
itemsWithTrackByCount = 0;
itemsWithTrackByCountReset = 0;
itemIdIncrement = 1;
currentClasses: Record<string, boolean> = {};
currentStyles: Record<string, string> = {};
ngOnInit() {
this.resetItems();
this.setCurrentClasses();
this.setCurrentStyles();
this.itemsNoTrackByCount = 0;
}
setUppercaseName(name: string) {
this.currentItem.name = name.toUpperCase();
}
setCurrentClasses() {
// CSS classes: added/removed per current state of component properties
this.currentClasses = {
saveable: this.canSave,
modified: !this.isUnchanged,
special: this.isSpecial,
};
}
setCurrentStyles() {
// CSS styles: set per current state of component properties
this.currentStyles = {
'font-style': this.canSave ? 'italic' : 'normal',
'font-weight': !this.isUnchanged ? 'bold' : 'normal',
'font-size': this.isSpecial ? '24px' : '12px',
};
}
isActiveToggle() {
this.isActive = !this.isActive;
}
giveNullCustomerValue() {
this.nullCustomer = 'Kelly';
}
resetItems() {
this.items = Item.items.map((item) => item.clone());
this.currentItem = this.items[0];
this.item = this.currentItem;
}
resetList() {
this.resetItems();
this.itemsWithTrackByCountReset = 0;
this.itemsNoTrackByCount = ++this.itemsNoTrackByCount;
}
changeIds() {
this.items.forEach((i) => (i.id += 1 * this.itemIdIncrement));
this.itemsWithTrackByCountReset = -1;
this.itemsNoTrackByCount = ++this.itemsNoTrackByCount;
this.itemsWithTrackByCount = ++this.itemsWithTrackByCount;
}
clearTrackByCounts() {
this.resetItems();
this.itemsNoTrackByCount = 0;
this.itemsWithTrackByCount = 0;
this.itemIdIncrement = 1;
}
trackByItems(index: number, item: Item): number {
return item.id;
}
trackById(index: number, item: any): number {
return item.id;
}
getValue(event: Event): string {
return (event.target as HTMLInputElement).value;
}
}
<label for="example-ngModel">[(ngModel)]:</label>
<input [(ngModel)]="currentItem.name" id="example-ngModel">
Structural Directives
Angular’s documentation: HTML layout is controlled by structural directives. By adding, deleting, and modifying the host elements to which they are attached, they usually modify the structure of the DOM. Common Structural Directives are, NgIf, NgFor and NgSwitch. Probably—by intuition—the If, For and Switch are familiar thing to you they control how the program flows but in this case, the structure of the HTML.
NgIf: builds or removes subviews from the template conditionally.
import {NgIf} from '@angular/common';
...
@Component({
standalone: true,
...
NgIf, // <-- import into the component
...
],
})
export class AppComponent implements OnInit {
...
}
<!-- NgIf appends the ItemDetailComponent to the DOM in response
to the isActive expression returning a truthy value. NgIf deletes
the ItemDetailComponent from the DOM and gets rid of the component
and all of its child components when the expression is false. -->
<app-item-detail *ngIf="isActive" [item]="item"></app-item-detail>
NgFor:Use theNgFordirective to present a list of items. For every entry in a list, repeat a node.
import {NgFor} from '@angular/common';
...
@Component({
standalone: true,
...
NgFor, // <-- import into the component
...
],
})
export class AppComponent implements OnInit {
...
}
<div *ngFor="let item of items">{{ item.name }}</div>
<!--
The following commands are sent to Angular by the string "let item of items":
1. Each item should be kept in the local item looping variable's items array.
2. For every iteration, make every object accessible to the templated HTML.
3. Translate "let item of items" to encircle the host element with a <ng-template.
4. For every item in the list, repeat the <ng-template.
-- >
NgSwitch: Similar to the JavaScript switch statement,NgSwitchuses a switch condition to display one element out of multiple potential elements. Only the chosen element is added to the DOM by Angular.
import {NgSwitch, NgSwitchCase, NgSwitchDefault} from '@angular/common';
...
@Component({
standalone: true,
...
NgSwitch, // <-- import into the component
NgSwitchCase,
NgSwitchDefault,
...
],
})
export class AppComponent implements OnInit {
...
}
<div [ngSwitch]="currentItem.feature">
<app-stout-item *ngSwitchCase="'stout'" [item]="currentItem"></app-stout-item>
<app-device-item *ngSwitchCase="'slim'" [item]="currentItem"></app-device-item>
<app-lost-item *ngSwitchCase="'vintage'" [item]="currentItem"></app-lost-item>
<app-best-item *ngSwitchCase="'bright'" [item]="currentItem"></app-best-item>
...
<app-unknown-item *ngSwitchDefault [item]="currentItem"></app-unknown-item>
</div>
<!-- This is almost the same as NgIf but more comprehensive
aan follows the same structure expected on a switch-case statement -->