Skip to main content

Directives

  • it is used to change the behavior and appearance of DOM element
  • it can implement all lifecycle hooks
  • it cannot have template

Structural Directives

*ngFor

  • allows looping of array

parent

  • tasks.component.ts
import {Component, OnInit} from "@angular/core";
import {Task} from "src/app/Task";
import {TASKS} from "src/app/mock-tasks";

@Component({
selector: "app-tasks",
templateUrl: "./tasks.component.html",
styleUrls: ["./tasks.component.css"],
})
export class TasksComponent implements OnInit {
tasks: Task[] = TASKS;

constructor() {}

ngOnInit(): void {}
}
  • tasks.component.html
<app-task-item *ngFor="let task of tasks" [task]="task"> </app-task-item>

child

  • task-item.component.ts
import {Component, OnInit, Input} from "@angular/core";
import {Task} from "src/app/Task";

@Component({
selector: "app-task-item",
templateUrl: "./task-item.component.html",
styleUrls: ["./task-item.component.css"],
})
export class TaskItemComponent implements OnInit {
@Input()
task!: Task;

constructor() {}

ngOnInit(): void {}
}
  • task-item.component.html
<div class="task">
<h3>{{ task.text }}</h3>
<p>{{ task.day }}</p>
</div>

*ngIf

  • A structural directive that conditionally includes a template based on the value of an expression coerced to Boolean
    • When the expression evaluates to true, Angular renders the template provided in a then clause, and when false or null, Angular renders the template provided in an optional else clause
    • The default template for the else clause is blank

add-task.component.html

  • do not show form when showAddTask is false
<form *ngIf="showAddTask" class="add-form">
<div class="form-control">
<label for="text">Task</label>
<input
type="text"
name="text"
[(ngModel)]="text"
id="text"
placeholder="Add Task"
/>
</div>
</form>

add-task.component.ts

import {Component, OnInit, Output, EventEmitter} from "@angular/core";
import {Subscription} from "rxjs";
import {UiService} from "src/app/services/ui.service";
import {Task} from "src/app/Task";

@Component({
selector: "app-add-task",
templateUrl: "./add-task.component.html",
styleUrls: ["./add-task.component.css"],
})
export class AddTaskComponent implements OnInit {
@Output()
onAddTask: EventEmitter<Task> = new EventEmitter();
text!: string;
showAddTask: boolean = false;
subscription!: Subscription;

constructor(private uiService: UiService) {
this.subscription = this.uiService
.onToggle()
.subscribe((value: boolean) => (this.showAddTask = value));
}

ngOnInit(): void {}
}

*ngSwitch

  • app.component.html

    <input type="text" [(ngModel)]="num" />
    <div [ngSwitch]="num">
    <div *ngSwitchCase="'1'">One</div>
    <div *ngSwitchCase="'2'">Two</div>
    <div *ngSwitchCase="'3'">Three</div>
    <div *ngSwitchCase="'4'">Four</div>
    <div *ngSwitchCase="'5'">Five</div>
    <div *ngSwitchDefault>This is Default</div>
    </div>
  • app.component.ts

    import {Component} from "@angular/core";

    @Component({
    selector: "app-root",
    templateUrl: "./app.component.html",
    styleUrls: ["./app.component.css"],
    })
    export class AppComponent {
    num: number = 0;
    }
  • app.module.ts

    import {NgModule} from "@angular/core";
    import {BrowserModule} from "@angular/platform-browser";
    import {FormsModule} from "@angular/forms";

    import {AppComponent} from "./app.component";

    @NgModule({
    declarations: [AppComponent],
    imports: [BrowserModule, FormsModule],
    providers: [],
    bootstrap: [AppComponent],
    })
    export class AppModule {}

Attribute Directives

ngStyle

  • allow inline styling
<button [ngStyle]="{ 'background-color': 'white' }" class="btn">text</button>

ngClass

  • Adds and removes CSS classes on an HTML element

task-item.component.html

<!-- when task.reminder is true, reminder-class css class style will be activated-->
<div [ngClass]="{ reminder-class: task.reminder }" class="task">
<h3>{{ task.text }}</h3>
<p>{{ task.day }}</p>
</div>

task-item.component.ts

import {Component, OnInit, Input} from "@angular/core";
import {Task} from "src/app/Task";
import {faTimes} from "@fortawesome/free-solid-svg-icons";

@Component({
selector: "app-task-item",
templateUrl: "./task-item.component.html",
styleUrls: ["./task-item.component.css"],
})
export class TaskItemComponent implements OnInit {
@Input()
task!: Task;

constructor() {}

ngOnInit(): void {}
}

task-item.component.css

.task {
background: #f4f4f4;
margin: 5px;
padding: 10px 20px;
cursor: pointer;
}

.task.reminder-class {
border-left: 5px solid green;
}

.task h3 {
display: flex;
align-items: center;
justify-content: space-between;
}

ngModel

  • Creates a FormControl instance from a domain model and binds it to a form control element

app.module.ts

import {NgModule} from "@angular/core";
import {BrowserModule} from "@angular/platform-browser";
import {FormsModule} from "@angular/forms";

import {AppComponent} from "./app.component";
import {AddTaskComponent} from "./components/add-task/add-task.component";

@NgModule({
declarations: [AppComponent, AddTaskComponent],
imports: [BrowserModule, FormsModule],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}

add-task.component.ts

import {Component, OnInit} from "@angular/core";

@Component({
selector: "app-add-task",
templateUrl: "./add-task.component.html",
styleUrls: ["./add-task.component.css"],
})
export class AddTaskComponent implements OnInit {
text!: string;
day!: string;
reminder: boolean = false;

constructor() {}

ngOnInit(): void {}
}

add-task.component.html

<form class="add-form">
<div class="form-control">
<label for="text">Task</label>
<input
type="text"
name="text"
[(ngModel)]="text"
id="text"
placeholder="Add Task"
/>
</div>
<div class="form-control">
<label for="day">Day & Time</label>
<input
type="text"
name="day"
[(ngModel)]="day"
id="day"
placeholder="Add Day & Time"
/>
</div>
<div class="form-control form-control-check">
<label for="reminder">Set Reminder</label>
<input
type="checkbox"
name="reminder"
[(ngModel)]="reminder"
id="reminder"
/>
</div>
<input type="submit" value="Save Task" class="btn btn-block" />
</form>

ngSubmit

parent

  • tasks.component.html
<app-add-task (onAddTask)="addTask($event)"></app-add-task>
  • tasks.component.ts
import {Component, OnInit} from "@angular/core";
import {TaskService} from "src/app/services/task.service";
import {Task} from "src/app/Task";

@Component({
selector: "app-tasks",
templateUrl: "./tasks.component.html",
styleUrls: ["./tasks.component.css"],
})
export class TasksComponent implements OnInit {
tasks: Task[] = [];

constructor(private taskService: TaskService) {}

ngOnInit(): void {}

addTask(task: Task) {
// adds task to backend server
this.taskService
.addTask(task)
.subscribe((task: Task) => this.tasks.push(task));
}
}

child

  • add-task.component.html
<form class="add-form" (ngSubmit)="onSubmit()">
<div class="form-control">
<label for="text">Task</label>
<input
type="text"
name="text"
[(ngModel)]="text"
id="text"
placeholder="Add Task"
/>
</div>
<input type="submit" value="Save Task" class="btn btn-block" />
</form>
  • add-task.component.ts
import {Component, OnInit, Output, EventEmitter} from "@angular/core";
import {Task} from "src/app/Task";

@Component({
selector: "app-add-task",
templateUrl: "./add-task.component.html",
styleUrls: ["./add-task.component.css"],
})
export class AddTaskComponent implements OnInit {
@Output()
onAddTask: EventEmitter<Task> = new EventEmitter();
text!: string;

constructor() {}

ngOnInit(): void {}

onSubmit() {
if (!this.text) {
alert("Please add a task!");
return;
}

const newTask = {
text: this.text,
};

this.onAddTask.emit(newTask);

// reset input value
this.text = "";
}
}

Custom Directives

  • cli

    ng generate directive directives/somename

generated and modified code

  • src/app/directives/somename.directive.ts
import {Directive} from "@angular/core";

@Directive({
selector: "[appSomename]",
})
export class SomenameDirective {
constructor() {}
}
  • src/app/directives/somename.directive.spec.ts
import {SomenameDirective} from "./somename.directive";

describe("SomenameDirective", () => {
it("should create an instance", () => {
const directive = new SomenameDirective();
expect(directive).toBeTruthy();
});
});
  • src/app/app.module.ts
import {NgModule} from "@angular/core";
import {BrowserModule} from "@angular/platform-browser";

import {AppComponent} from "./app.component";
import {SomenameDirective} from "./directives/somename.directive";

@NgModule({
declarations: [AppComponent, SomenameDirective],
imports: [BrowserModule],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}

sample usage code

  • lifecycle hooks works as well

  • src/app/directives/somename.directive.ts

    • method 1: ElementRef

      import {Directive, ElementRef} from "@angular/core";

      @Directive({
      selector: "[appSomename]",
      })
      export class SomenameDirective {
      constructor(el: ElementRef) {
      const element = el.nativeElement;
      element.style.color = "red";
      }
      }
    • method 2: Document

      import {DOCUMENT} from "@angular/common";
      import {Directive, Inject} from "@angular/core";

      @Directive({
      selector: "[appSomename]",
      })
      export class SomenameDirective {
      constructor(@Inject(DOCUMENT) private document: Document) {
      const element = this.document.querySelector("p") as HTMLElement;
      element.style.color = "red";
      }
      }
    • method 3: ElementRef & Renderer2

      import {
      Directive,
      ElementRef,
      HostListener, // used for event listeners
      Renderer2,
      } from "@angular/core";

      @Directive({
      selector: "[appSomename]",
      })
      export class SomenameDirective {
      constructor(el: ElementRef, private renderer: Renderer2) {
      this.renderer.setStyle(this.el.nativeElement, "color", "red");
      }

      // used for event listener
      // method name can be anything
      @HostListener("mouseenter") onMouseEnter() {
      this.renderer.setStyle(
      this.el.nativeElement,
      "backgroundColor",
      "Black"
      );
      }
      }
  • app.component.html

    <p appSomename>custom directive</p>

sample usage code 2, lifecycle method required if using input

  • src/app/directives/somename.directive.ts

    import {Directive, OnInit, Input, ElementRef} from "@angular/core";

    @Directive({
    selector: "[appSomename]",
    })
    export class SomenameDirective implements OnInit {
    @Input()
    appSomename: string = "black";

    constructor(el: ElementRef) {
    // this will not work, only hard coding will work
    const element = el.nativeElement;
    element.style.color = this.appSomename;
    }

    ngOnInit(): void {
    const element = el.nativeElement;
    element.style.color = this.appSomename;
    }
    }
  • app.component.html

    <p appTesting="red">custom directive</p>