Angular 9 Tutorial: Angular Component Example

by Didin J. on Mar 11, 2020 Angular 9 Tutorial: Angular Component Example

The comprehensive Angular 9 tutorial about Angular Component with more examples to help you understanding Angular Components

In this Angular 9 tutorial, we will try to make an easy understanding of Angular Component by the examples. The Angular component is one of the basic building blocks of the Angular app. Before jump to the example, we will show you a basic knowledge of the Angular component in the beginning subtitles.

Table of contents:

The following tools, frameworks, and modules are required for this tutorial:

  1. Node.js
  2. Angular 9
  3. Angular CLI
  4. Terminal or Node Command Line
  5. Text Editor or IDE (We are using VSCode)

Before going to the main subtitles, we need to make sure that we have installed Node.js and NPM by type these commands to check them.

node -v
npm -v

You can watch the video tutorial on our YouTube channel.


What is Angular Component?

Angular Component is an Angular directive that has its own view or templates independently. Angular Component has access to all the directive API (eg: ngModel, ngClass, ngIf, ngFor, etc) that provided by Angular and still have a host element, can still define input and output properties, and so on. They also can define their own content.

Angular Component is closely tied to the content of its templates. Angular Component provides the data and logic that will be used by the data bindings that are applied to the HTML elements in the template, which provide the context used to evaluate data binding expressions and act as the glue between the directives and the rest of the application. Angular Component is also a useful Angular part in allowing large Angular projects to be modularized into manageable parts.

And here's how we describe an Angular Component.

Angular 9 Tutorial: Angular Component Example - diagram

  1. Angular Component can register or determine their own template/HTML, styles, and configuration metadata that determines how the component should be processed, instantiated, and used at runtime.
  2. Template or HTML can binding property and function provided by Component (eg: variable, function, etc).
  3. The component can a binding event that comes from the template (eg: Angular directives such as ngModel, ngClick, ngOnchange, ngClass, etc).

To use Angular Component just use "@Component" decorator before the current class name. The Angular @Component decorator is applied to a Typescript class, which is registered in the main  Angular application module.

Here's the Angular Component look like.

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

@Component({
  selector: 'app-root',
  template: `
    <div class="header">My Angular Component</div>
    <p>Hi {{yourname}}, Welcome to Angular World!</p>
    <p>You are the visitor number {{counter}}.</p>
    `,
  styles: ['header { font-weight: bold; font-size: 1.5em }']
})
export class AppComponent {
  yourname = 'John Doe';
  counter = 102;
}

That example uses the template and style in the same file on the component. 


Creating a New Angular 9 App

As usual, to make easier understanding is practicing the Angular @Component in the full Angular 9 app. We will create an Angular 9 app using Angular CLI. Make sure, you have installed the Angular CLI by type this command in the Terminal or Node command line.

sudo npm i -g @angular/cli

Now, we have an @angular/cli version 9.0.5 in your terminal or Node environment. Next, create a new Angular 9 app inside your Angular applications folder.

ng new angular-component

For now, leave all questions as default by press the Enter key. Next, go to the newly created Angular 9 application then open it with your Text Editor or IDE. To use VSCode, just type this command in this new Angular 9 app folder.

cd ./angular-component
code .

Now, the new Angular 9 app ready to develop. Next, open the "src/app/app.component.ts" to see the basic generated root component.

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

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'angular-component';
}

As you see, the above-generated root component have a separate template file (./app.component.html) and the style file (./app.component.css). The selector "app-root" is the initial Angular directive that use to make this component as a starting component when this Angular app run. This directive used in the "src/index.html". Also, it has an "app.component.spec.ts" in the same directory that uses as the test unit for this Angular Component.


Creating a New Component

We can create a new component manually and using Angular CLI schematic to generate component. Starting to create a new component manually by creating a new folder inside the "src/app/" folder with the folder name similar to the component name to make it easier to manage.

mkdir src/app/sales

Next, create a Typescript file inside that folder.

touch src/app/sales/sales.component.ts

To make a separate template and style, make the new files with HTML and CSS extension on the same folder as the Typescript file.

touch src/app/sales/sales.component.html
touch src/app/sales/sales.component.css

You also can create a test unit file for this component in the same folder.

touch src/app/sales/sales.component.spec.ts

Next, open and edit "src/app/sales/sales.component.ts" then add these lines of Typescript codes.

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

@Component({
  selector: 'app-sales',
  templateUrl: './sales.component.html',
  styleUrls: ['./sales.component.css']
})
export class SalesComponent implements OnInit {

  constructor() { }

  ngOnInit(): void {
  }

}

As you see, the class name has suffix ...Component and @Component that indicate this is an Angular Component class. It has the directive selector "app-sales" that can use to access this Component. It uses separate template and style files. Additionally, we add the Angular Component lifecycle NgOnInit function as implemented in the class name and a constructor that injectable for other modules that will use in this Component. Next, we will put variables and functions to this Angular Component. Add this string and number date variable at the top of the class body.

  title = 'Sales Report';
  created: Date = new Date();
  subtitle = '';

Add a simple function after the Angular NgOnInit function.

  salesDate() {
    this.subtitle = `${this.title} of ${this.datePipe.transform(this.created, 'short')}`;
  }

That function required the Datapipe module. For that, add built-in Angular DatePipe module import and inject it to the constructor.

import { DatePipe } from '@angular/common';
...
constructor(private datePipe: DatePipe) { }

Call that function inside NgOnInit to make it always fresh.

  ngOnInit(): void {
    this.salesDate();
  }

Next, open and edit "src/app/sales/sales.component.html" then add these lines of HTML tags and binding Angular variables.

<h1 class="header">{{title}}</h1>
<h3 class="sub-header">{{created}}</h3>
<p class="content">{{subtitle}}</p>

Next, open and edit "src/app/sales/sales.component.css" then add these lines of CSS codes that previously declared in the HTML.

.header {
  font-weight: 500;
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
  font-size: 1.6em;
}

.sub-header {
  font-weight: 300;
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
  font-size: 1em;
  color: #aaaaaa;
}

.content {
  font-weight: normal;
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
  font-size: 0.6;
}

Next, open and edit "src/app/sales/sales.component.spec.ts" then add these lines of Typescript codes that contain simple test unit script for the SalesComponent.

import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { SalesComponent } from './sales.component';

describe('SalesComponent', () => {
  let component: SalesComponent;
  let fixture: ComponentFixture<SalesComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ SalesComponent ]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(SalesComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });
});

Next, register this new component and DatePipe module by open and edit "src/app/app.module.ts" then add this import.

import { SalesComponent } from './sales/sales.component';
import { DatePipe } from '@angular/common';

Add that component to @NgModule declarations.

@NgModule({
  declarations: [
    ...
    SalesComponent
  ], 
  ...
})

And DatePipe to @NgModule providers.

@NgModule({
  ...
  providers: [DatePipe],
  ...
})

Now, the new manually created Angular Component is ready to use. You can check this new component by make it as the starting component. To make this new component as the starting point, in "app.module.ts" change the @NgModule bootstrap to this.

bootstrap: [SalesComponent]

Then open and edit "src/index.html" then change <app-root> directive to this.

<app-sales></app-sales>

Run this Angular 9 app to see the changes.

ng serve --open

And this is a starting component template preview.

Angular 9 Tutorial: Angular Component Example - component template

The easiest and quick way to create or generate an Angular component is using Angular CLI. Just type this command in the terminal or Node command line.

ng g component item

Now, we have ItemComponent with a separate template, style, and test unit. Also, it registered automatically in "app.module.ts". So, you just fill this component with logic, view, and style.

Binding Component Properties in Template

An Angular component’s template can contain the variety of data bindings and target any of the built-in or custom directives that have been registered in the "app.module.ts". The component class has a mechanism to evaluate the data binding expressions in its template and it's isolated from the other Components. So, you don't have to worry about variable or property name will conflict with the other Components properties.

Actually, in the previous example, there's already the properties and binding in the template. But for now, we will create again the various properties such as string, number, date, and array. Open and edit "src/app/item/item.component.ts" then add these lines of properties or variables.

  title = 'Item List';
  date: Date = new Date();
  simpleDate: string;
  discount = 20;
  items: any[] = [
    { id: 1, name: 'iPhone X', desc: 'Refurbished iPhone X 2019', price: 399 },
    { id: 2, name: 'iPhone 11 Pro', desc: 'The latest iPhone series', price: 1099 },
    { id: 3, name: 'Samsung S20', desc: 'The latest Samsung Galaxy S series in 2020', price: 1199 },
    { id: 4, name: 'Asus ROG Phone 2', desc: 'The gaming phone from Asus', price: 599 },
    { id: 5, name: 'Nokia 9', desc: 'The latest Nokia phone in 2020', price: 799 },
  ];

Like the previous example, we use again DatePipe. For that, add this import of DatePipe.

import { DatePipe } from '@angular/common';

Inject DatePipe to the constructor.

constructor(private datePipe: DatePipe) { }

Next, we will bind those declared properties in the template. Open and edit "src/app/item/item.component.html" then replace all HTML tags with this.

<div class="container">
  <h1 class="header">{{title}}</h1>
  <h3 class="sub-header">{{simpleDate}}</h3>
  <div class="content">
    <table>
      <thead>
        <tr>
          <th>ID</th>
          <th>Item Name</th>
          <th>Item Description</th>
          <th>Item Price</th>
          <th>Discount</th>
          <th>Total Price</th>
        </tr>
      </thead>
      <tbody>
        <tr *ngFor="let item of items">
          <td>{{item.id}}</td>
          <td>{{item.name}}</td>
          <td>{{item.desc}}</td>
          <td>{{item.price | currency: 'USD'}}</td>
          <td>{{discount}}%</td>
          <td>{{item.price - ((item.price * discount) / 100) | currency: 'USD'}}</td>
        </tr>
      </tbody>
    </table>
  </div>
</div>

As you see, data binding in the title, subtitle, and table row. We put built-in Angular 9 CurrencyPipe in the item price and total price. Next, open and edit "src/app/item/item.component.css" then add style for the above template.

.container {
  padding: 20px;
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
.header {
  color: #333333;
  font-size: 1.6em;
}
.sub-header {
  color: #999999;
  font-size: 1em;
  font-style: italic;
}
.content table {
  padding: 0;
  margin: 0;
  border-collapse: collapse;
}
.content table tr th {
  border-bottom: solid 3px #777777;
  padding: 5px 10px;
}
.content table tr td {
  padding: 5px 10px;
}
.content table tr:last-child td {
  border-bottom: solid 1px #777777;
}
.content table tr td:nth-child(4), .content table tr td:nth-child(5), .content table tr td:nth-child(6) {
  text-align: right;
}

Now, the template shows a table like this.

Angular 9 Tutorial: Angular Component Example - table


Binding User Actions from Template to Component

There are various ways to binding user actions from the template to the component. They can be a click, change, and select actions or DOM events of the entering input text. Let start's with the simple (click) action from a Button. Still on "item.component.html" add this line of HTML tag after <h3> tag.

<button (click)="updateDate()">Refresh Date</button>

Next, open and edit "src/app/item/item.component.ts" then add this function.

  updateDate() {
    this.date = new Date();
    this.simpleDate = this.datePipe.transform(this.date, 'short');
  }

When this Angular 9 app runs, you can click the Refresh Date button and it will trigger the "updateDate" function in the Component then you will see the time of the date changes as the actual time in your computer. Another example binding (click) to the component function/method is added (click) action to the table row.

        <tr *ngFor="let item of items" (click)="selectData(item)">
          ...
        </tr>

In the Component add this function or method to bind the selected item from the table in the template. This function will show an alert that contains the selected items.

  selectData(item: any) {
    alert(`ID: ${item.id}, Name: ${item.name}, Desc: ${item.desc}, Price: ${item.price}`);
  }

Next, to implementing DOM events, we will use the table and mouseover event. Add this (mouseover) to the table row and add the new HTML tag to show the selected text in the table.

        <tr *ngFor="let item of items" (click)="selectData(item)" (mouseover)="hoverRow($event)">
          ...
        </tr>

...
<p>{{selected}}</p>

In the component, add this property and function to respond to the binding event from the mouseover DOM event. The response will show selected text from the column that hovered by the mouse pointer.

  selected: string;
  ...
  hoverRow(event: any) {
    this.selected = event.target.innerText;
  }

Now, when the mouse over the column on the table, the <p> tags will show the selected column text.


Component Lifecycle Hooks Examples

As you find on every Frontend framework, there are the lifecycle hooks of the components that check when the page init, load, change, close/destroy. For these examples, we will create the component for each lifecycle hooks. Type these commands to create them.

ng g component ng-onchanges-example
ng g component ng-onchangesparent
ng g component ng-oninit-example
ng g component do-check-example
ng g component do-check-parent
ng g component ng-aftercontentinit-example
ng g component ng-aftercontentchecked-example
ng g component ng-afterviewinit-example
ng g component ng-afterviewchecked-example
ng g component ng-ondestroy-example

ngOnInit() Example

Open and edit the "src/app/ng-oninit-example/ng-oninit-example.component.ts" then replace all Typescript codes with this.

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

@Component({
  selector: 'app-ng-oninit-example',
  templateUrl: './ng-oninit-example.component.html',
  styleUrls: ['./ng-oninit-example.component.css']
})
export class NgOninitExampleComponent implements OnInit {

  currentDate: Date;

  constructor() { }

  ngOnInit(): void {
    this.currentDate = new Date();
  }

}

Open and edit the "src/app/ng-oninit-example/ng-oninit-example.component.html" then replace all HTML tags with this.

<h1>ngOnInit() Example</h1>
<h3>{{currentDate | date: 'short'}}</h3>

ngOnChanges() Example

Open and edit "src/app/ng-onchanges-example/ng-onchanges-example.component.ts" then replace all Typescript codes with these.

import { Component, Input, OnChanges, SimpleChanges, ViewChild } from '@angular/core';

@Component({
  selector: 'app-ng-onchanges-example',
  templateUrl: './ng-onchanges-example.component.html',
  styleUrls: ['./ng-onchanges-example.component.css']
})
export class NgOnchangesExampleComponent implements OnChanges {

  @Input() cost: number;
  @Input() margin: number;
  @Input() price: number;

  changeLog: string[] = [];

  constructor() { }

  ngOnChanges(changes: SimpleChanges): void {
    for (const propName in changes) {
      if (changes.hasOwnProperty(propName)) {
        const chng = changes[propName];
        const cur  = JSON.stringify(chng.currentValue);
        const prev = JSON.stringify(chng.previousValue);
        this.changeLog.push(`${propName}: currentValue = ${cur}, previousValue = ${prev}`);
      }
    }
  }

  clear() { this.changeLog = []; }

}

Open and edit "src/app/ng-onchanges-example/ng-onchanges-example.component.html" then replace all HTML tags with these.

<h1>ngOnChanges() Example</h1>
<p>cost + margin = price</p>
<p>{{cost}} + {{margin}} = {{price}}</p>
<h4>-- Change Log --</h4>
<ul>
  <li *ngFor="let chg of changeLog">{{chg}}</li>
</ul>

Open and edit "src/aoo/ng-onchanghesparent.component.ts" then replace all Typescript codes with this.

import { Component, ViewChild } from '@angular/core';
import { NgOnchangesExampleComponent } from '../ng-onchanges-example/ng-onchanges-example.component';

@Component({
  selector: 'app-ng-onchangesparent',
  templateUrl: './ng-onchangesparent.component.html',
  styleUrls: ['./ng-onchangesparent.component.css']
})
export class NgOnchangesparentComponent {

  cost: number;
  margin: number;
  price: number;
  @ViewChild(NgOnchangesExampleComponent) childView: NgOnchangesExampleComponent;

  constructor() {
    this.clear();
  }

  clear() {
    this.cost = 100;
    this.margin = 10;
    this.price = 110;
    if (this.childView) { this.childView.clear(); }
  }

}

Open and edit "src/aoo/ng-onchanghesparent.component.html" then replace all HTML tags with this.

<p>
  Cost: <br>
  <input type="number" [(ngModel)]="cost" /><br>
  Margin: <br>
  <input type="number" [(ngModel)]="margin" /><br>
  Price: <br>
  <input type="number" [(ngModel)]="price" /><br>

  <app-ng-onchanges-example [cost]="cost" [margin]="margin" [price]="price"></app-ng-onchanges-example><br>
</p>

ngDoCheck() Example

Open and edit "src/app/do-check-example/do-check-example.component.ts" then replace all Typescript codes with this.

import { Component, DoCheck, Input } from '@angular/core';

@Component({
  selector: 'app-do-check-example',
  templateUrl: './do-check-example.component.html',
  styleUrls: ['./do-check-example.component.css']
})
export class DoCheckExampleComponent implements DoCheck {

  @Input() cost: number;
  @Input() margin: string;

  changeDetected = false;
  changeLog: string[] = [];
  oldCost = null;
  oldMargin = null;
  oldLogLength = 0;
  noChangeCount = 0;

  ngDoCheck() {

    if (this.cost !== this.oldCost) {
      this.changeDetected = true;
      this.changeLog.push(`DoCheck: Cost changed to "${this.cost}" from previously "${this.oldCost}"`);
      this.oldCost = this.cost;
    }

    if (this.margin !== this.oldMargin) {
      this.changeDetected = true;
      this.changeLog.push(`DoCheck: Margin changed to "${this.margin}" from previously "${this.oldMargin}"`);
      this.oldMargin = this.margin;
    }

    if (this.changeDetected) {
        this.noChangeCount = 0;
    } else {
        const count = this.noChangeCount += 1;
        const noChangeMsg = `DoCheck called ${count}x when no change to hero or power`;
        if (count === 1) {
          this.changeLog.push(noChangeMsg);
        } else {
          this.changeLog[this.changeLog.length - 1] = noChangeMsg;
        }
    }

    this.changeDetected = false;
  }

  clean() {
    this.changeDetected = true;
    this.changeLog = [];
  }

}

Open and edit "src/app/do-check-example/do-check-example.component.html" then replace all HTML tags with this.

<h1>ngDoCheck() Example</h1>
<div>
  <p>Cost: {{cost}}<br>
  Margin: {{margin}}<br></p>

  <h4>-- Change Log --</h4>
  <ul>
    <li *ngFor="let chg of changeLog">{{chg}}</li>
  </ul>
</div>

Open and edit "src/app/do-check-parent/do-check-parent.component.ts" then replace all Typescript codes with this.

import { Component, ViewChild } from '@angular/core';
import { DoCheckExampleComponent } from '../do-check-example/do-check-example.component';

@Component({
  selector: 'app-do-check-parent',
  templateUrl: './do-check-parent.component.html',
  styleUrls: ['./do-check-parent.component.css']
})
export class DoCheckParentComponent {

  cost: number;
  margin: number;
  @ViewChild(DoCheckExampleComponent) childView: DoCheckExampleComponent;

  constructor() { this.clean(); }

  clean() {
    this.cost = 599;
    this.margin = 15;
    if (this.childView) { this.childView.clean(); }
  }

}

Open and edit "src/app/do-check-parent/do-check-parent.component.html" then replace all HTML tags with this.

<div>
  <h2>DoCheck Parent</h2>

  <p>
    Cost: <input type="number" [(ngModel)]="cost" /><br>
    Margin: <input type="number" [(ngModel)]="margin" />
  </p>
  <p><button (click)="clean()">Clean Log</button></p>

  <app-do-check-example [cost]="cost" [margin]="margin"></app-do-check-example>
</div>

ngAfterContentChecked() Example

Open and edit "src/app/ng-aftercontentchecked-example/ng-aftercontentchecked-example.component.ts" then replace all Typescript codes with these.

import { Component, AfterContentChecked, ContentChild } from '@angular/core';

@Component({
  selector: 'app-after-content-check-child',
  template: '<input [(ngModel)]="price">'
})
export class AfterContentCheckChildComponent {
  price = 1099;
}

@Component({
  selector: 'app-ng-aftercontentchecked-example',
  templateUrl: './ng-aftercontentchecked-example.component.html',
  styleUrls: ['./ng-aftercontentchecked-example.component.css']
})
export class NgAftercontentcheckedExampleComponent implements AfterContentChecked {

  private prevPrice = null;
  status = '';

  @ContentChild(AfterContentCheckChildComponent) contentChild: AfterContentCheckChildComponent;

  constructor() { }

  ngAfterContentChecked() {
    if (this.prevPrice === this.contentChild.price) {
      console.log('AfterContentChecked (no change)');
    } else {
      this.prevPrice = this.contentChild.price;
      console.log('AfterContentChecked');
      this.changeStatus();
    }
  }

  private changeStatus() {
    this.status = 'This price ' + this.contentChild.price + ' is a basic price';
  }

}

@Component({
  selector: 'app-after-content-parent',
  template: `
  <div>
    <h2>AfterContent</h2>
    <div *ngIf="show">
      <app-ng-aftercontentchecked-example>
        <app-after-content-check-child></app-after-content-check-child>
      </app-ng-aftercontentchecked-example>
    </div>
    <h4>-- AfterContent Logs --</h4>
    <p><button (click)="reset()">Reset</button></p>
  </div>
  `,
  styles: ['.parent {background: burlywood}']
})
export class AfterContentCheckParentComponent {
  show = true;

  constructor() {
  }

  reset() {
    this.show = false;
  }
}

Open and edit "src/app/ng-aftercontentchecked-example/ng-aftercontentchecked-example.component.html" then replace all HTML tags with these.

<h1>ngAfterContentChecked() Example</h1>
<div>-- projected content begins --</div>
  <ng-content></ng-content>
<div>-- projected content ends --</div>`

<p *ngIf="status">
  {{status}}
</p>

Don't forget to add import and @NgModule declaration in "app.module.ts".

import { NgAftercontentcheckedExampleComponent,
  AfterContentCheckChildComponent,
  AfterContentCheckParentComponent } from './ng-aftercontentchecked-example/ng-aftercontentchecked-example.component';

@NgModule({
  declarations: [
    ...
    AfterContentCheckChildComponent,
    AfterContentCheckParentComponent,
  ...
})

ngAfterContentInit() Example

Open and edit "src/app/ng-aftercontentinit-example/ng-aftercontentinit-example.component.ts" then replace all Typescript codes with this.

import { Component, AfterContentInit, ContentChild } from '@angular/core';

@Component({
  selector: 'app-after-content-init-child',
  template: '<input [(ngModel)]="price">'
})
export class AfterContentInitChildComponent {
  price = 1099;
}

@Component({
  selector: 'app-ng-aftercontentinit-example',
  templateUrl: './ng-aftercontentinit-example.component.html',
  styleUrls: ['./ng-aftercontentinit-example.component.css']
})
export class NgAftercontentinitExampleComponent implements AfterContentInit {

  private prevPrice = null;
  status = '';

  @ContentChild(AfterContentInitChildComponent) contentChild: AfterContentInitChildComponent;

  constructor() { }

  ngAfterContentInit() {
    this.changeStatus();
  }

  private changeStatus() {
    this.status = 'This price ' + this.contentChild.price + ' is a basic price';
  }

}

@Component({
  selector: 'app-after-content-parent',
  template: `
  <div>
    <h2>AfterContent</h2>
    <div *ngIf="show">
      <app-ng-aftercontentinit-example>
        <app-after-content-init-child></app-after-content-init-child>
      </app-ng-aftercontentinit-example>
    </div>
    <h4>-- AfterContent Logs --</h4>
    <p><button (click)="reset()">Reset</button></p>
  </div>
  `,
  styles: ['.parent {background: burlywood}']
})
export class AfterContentInitParentComponent {
  show = true;

  constructor() {
  }

  reset() {
    this.show = false;
  }
}

Open and edit "src/app/ng-aftercontentinit-example/ng-aftercontentinit-example.component.html" then replace all HTML tags with this.

<h1>ngAfterContentInit() Example</h1>
<div>-- projected content begins --</div>
  <ng-content></ng-content>
<div>-- projected content ends --</div>`

<p *ngIf="status">
  {{status}}
</p>

Don't forget to add import and @NgModule declaration in "app.module.ts".

import { NgAftercontentinitExampleComponent,
  AfterContentInitChildComponent,
  AfterContentInitParentComponent } from './ng-aftercontentinit-example/ng-aftercontentinit-example.component';

@NgModule({
  declarations: [
    ...
    AfterContentInitChildComponent,
    AfterContentInitParentComponent,
  ...
})

ngAfterViewChecked() Example

Open and edit "src/app/ng-afterviewchecked-example/ng-afterviewchecked-example.component.ts" then replace all Typescript codes with this.

import { AfterViewChecked, Component, ViewChild } from '@angular/core';

@Component({
  selector: 'app-after-view-checked-child',
  template: '<input [(ngModel)]="price">'
})
export class AfterViewCheckedChildComponent {
  price = 599;
}

@Component({
  selector: 'app-ng-afterviewchecked-example',
  templateUrl: './ng-afterviewchecked-example.component.html',
  styleUrls: ['./ng-afterviewchecked-example.component.css']
})
export class NgAfterviewcheckedExampleComponent implements AfterViewChecked {

  private prevPrice = null;
  status = '';

  @ViewChild(AfterViewCheckedChildComponent) viewChild: AfterViewCheckedChildComponent;

  constructor() { }

  ngAfterViewChecked() {
    if (this.prevPrice === this.viewChild.price) {
      console.log('AfterViewChecked (no change)');
    } else {
      this.prevPrice = this.viewChild.price;
      console.log('AfterViewChecked');
      this.changeStatus();
    }
  }

  private changeStatus() {
    const s = 'This price ' + this.viewChild.price + ' is the total price include tax.';
    if (s !== this.status) {
      console.log(s);
    }
  }
}

@Component({
  selector: 'app-after-view-checked-parent',
  template: `
  <div>
    <h2>AfterView Checked Parent</h2>

    <app-ng-afterviewchecked-example  *ngIf="show"></app-ng-afterviewchecked-example>

    <p><button (click)="clear()">Clear</button></p>
  </div>
  `,
  styles: ['.parent {background: burlywood}']
})
export class AfterViewCheckedParentComponent {
  show = true;

  constructor() {
  }

  clear() {
    this.show = false;
  }
}

Open and edit "src/app/ng-afterviewchecked-example/ng-afterviewchecked-example.component.html" then replace all HTML tags with this.

<h1>ngAfterViewChecked() Example</h1>
<div>-- child view begins --</div>
<app-after-view-checked-child></app-after-view-checked-child>
<div>-- child view ends --</div>
<p *ngIf="status">
{{status}}
</p>

Don't forget to add import and @NgModule declaration in "app.module.ts".

import { NgAfterviewcheckedExampleComponent,
  AfterViewCheckedChildComponent,
  AfterViewCheckedParentComponent } from './ng-afterviewchecked-example/ng-afterviewchecked-example.component';

@NgModule({
  declarations: [
    ...
    AfterViewCheckedChildComponent,
    AfterViewCheckedParentComponent,
  ...
})

ngAfterViewInit() Example

Open and edit "src/aoo/ng-afterviewinit-example/ng-afterviewinit-example.component.ts" then replace all Typescript code with this.

import { AfterViewInit, Component, ViewChild } from '@angular/core';

@Component({
  selector: 'app-after-view-init-child',
  template: '<input [(ngModel)]="price">'
})
export class AfterViewInitChildComponent {
  price = 599;
}

@Component({
  selector: 'app-ng-afterviewinit-example',
  templateUrl: './ng-afterviewinit-example.component.html',
  styleUrls: ['./ng-afterviewinit-example.component.css']
})
export class NgAfterviewinitExampleComponent implements AfterViewInit {

  private prevPrice = null;
  status = '';

  @ViewChild(AfterViewInitChildComponent) viewChild: AfterViewInitChildComponent;

  constructor() { }

  ngAfterViewInit() {
    this.changeStatus();
  }

  private changeStatus() {
    const s = 'This price ' + this.viewChild.price + ' is the total price include tax.';
    if (s !== this.status) {
      console.log(s);
    }
  }

}

@Component({
  selector: 'app-after-view-init-parent',
  template: `
  <div>
    <h2>AfterView Init Parent</h2>

    <app-ng-afterviewinit-example  *ngIf="show"></app-ng-afterviewinit-example>

    <p><button (click)="clear()">Clear</button></p>
  </div>
  `,
  styles: ['.parent {background: burlywood}']
})
export class AfterViewInitParentComponent {
  show = true;

  constructor() {
  }

  clear() {
    this.show = false;
  }
}

Open and edit "src/aoo/ng-afterviewinit-example/ng-afterviewinit-example.component.html" then replace all HTML tags with this.

<h1>ngAfterViewChecked() Example</h1>
<div>-- child view begins --</div>
<app-after-view-checked-child></app-after-view-checked-child>
<div>-- child view ends --</div>
<p *ngIf="status">
{{status}}
</p>

ngOnDestoy() Example

Open and edit "src/app/ng-ondestroy-example/ng-ondestroy-example.component.ts" then replace all Typescript codes with this.

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

@Component({
  selector: 'app-ng-ondestroy-example',
  templateUrl: './ng-ondestroy-example.component.html',
  styleUrls: ['./ng-ondestroy-example.component.css']
})
export class NgOndestroyExampleComponent implements OnInit, OnDestroy {

  showTime: string;

  constructor() { }

  ngOnInit(): void {
    this.showTime = new Date().toString();
  }

  ngOnDestroy(): void {
    this.showTime = '';
  }

}

Open and edit "src/app/ng-ondestroy-example/ng-ondestroy-example.component.html" then replace all HTML tags with this.

<h1>ngOnDestroy() Example</h1>
<h3>{{showTime | date: 'short'}}</h3>


Communication Between Components

In Angular 9, components can communicate or share data between them. Communication or share data can use @Input binding, Event Emitter, or communicate via a service. As usual, you will learn this Components communication by examples.

Before going to the examples, we need to generate the required components and service by type these commands.

ng g component teacher
ng g component student
ng g component lesson
ng g component student-lesson
ng g component products
ng g component product-details
ng g component store
ng g component customer
ng g service order

Pass data between components using @Input binding

Open and edit "src/app/teacher/teacher.component.ts" then replace all Typescript codes with this.

import { Component, Input } from '@angular/core';

@Component({
  selector: 'app-teacher',
  templateUrl: './teacher.component.html',
  styleUrls: ['./teacher.component.css']
})
export class TeacherComponent {

  @Input() lesson: any[] = [
    { name: 'Calculus', credits: 4 },
    { name: 'Logic Math', credits: 3 },
    { name: 'Numeric System', credits: 2 }
  ];
  @Input() name: string;

  constructor() { }

}

Open and edit "src/app/teacher/teacher.component.html" then replace all HTML tags with this.

<h1>Pass data Example 1</h1>
<h3>{{name}}</h3>
<ul>
  <li *ngFor="let ls of lesson">{{ls.name}} ({{ls.credits}}</li>
</ul>

Open and edit "src/app/student/student.component.ts" then replace all Typescript codes with this.

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

@Component({
  selector: 'app-student',
  templateUrl: './student.component.html',
  styleUrls: ['./student.component.css']
})
export class StudentComponent {

  lesson: any[] = [];
  name = '';

  constructor() { }

}

Open and edit "src/app/student/student.component.html" then replace all HTML tags with this.

<h1>Pass data Example 1</h1>
<app-teacher *ngFor="let ls of lesson"
  [name]="name"
  [lesson]="lesson">
</app-teacher>

Intercept @Input Property Changes

In this example, the @Input will be intercepted by setting the property to other string when it's empty or blank space. Open and edit "src/app/lesson/lesson.component.ts" then replace all Typescript codes with this.

import { Component, Input } from '@angular/core';

@Component({
  selector: 'app-lesson',
  templateUrl: './lesson.component.html',
  styleUrls: ['./lesson.component.css']
})
export class LessonComponent{

  // declare property
  private pickLesson = '';

  @Input()
  set lesson(lsn: string) {
    this.pickLesson = (lsn && lsn.trim() || 'No lesson available today');
  }

  get lesson(): string { return this.pickLesson; }

}

Open and edit "src/app/lesson/lesson.component.html" then replace all HTML tags with this.

<h1>Intercept @Input Example</h1>
<h3>{{lesson}}</h3>

Open and edit "src/app/student-lesson/student-lesson.component.ts" then replace all Typescript codes with this.

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

@Component({
  selector: 'app-student-lesson',
  templateUrl: './student-lesson.component.html',
  styleUrls: ['./student-lesson.component.css']
})
export class StudentLessonComponent {

  lessons = ['Calculus', 'Logic Math', 'Algorithm', ' '];

}

Open and edit "src/app/student-lesson/student-lesson.component.html" then replace all HTML tags with this.

<h3>{{lessons}}</h3>

Send and Receive Events using EventEmitter

Open and edit "src/app/products/products.component.ts" then replace all Typescript codes with this.

import { Component, EventEmitter, Input, Output } from '@angular/core';

@Component({
  selector: 'app-products',
  templateUrl: './products.component.html',
  styleUrls: ['./products.component.css']
})
export class ProductsComponent {

  @Input()  product: any;
  @Output() prodDet = new EventEmitter<boolean>();

  showDetails(prod: any) {
    this.prodDet.emit(prod);
  }

}

Open and edit "src/app/products/products.component.html" then replace all HTML tags with this.

<li>{{product.productName}}, <button (click)="showDetails(product)">Show Details</button></li>

Open and edit "src/app/product-details/product-details.component.ts" then replace all Typescript codes with this.

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

@Component({
  selector: 'app-product-details',
  templateUrl: './product-details.component.html',
  styleUrls: ['./product-details.component.css']
})
export class ProductDetailsComponent {

  productName = '';
  productDesc = '';
  productPrice = null;
  products: any[] = [
    { productName: 'eMac', productDesc: 'Old Mac computer', productPrice: 39 },
    { productName: 'Power Mac G4', productDesc: 'Old Mac computer with G4 Processor', productPrice: 39 },
    { productName: 'Power Mac G5', productDesc: 'Old Mac computer with G5 Processor', productPrice: 39 }
  ];

  showDetails(prod: any) {
    this.productName = prod.productName;
    this.productDesc = prod.productDesc;
    this.productPrice = prod.productPrice;
  }

}

Open and edit "src/app/product-details/product-details.component.html" then replace all HTML tags with this.

<h1>Send and Receive Events using EventEmitter</h1>
<dl>
  <dt>Product Name:</dt>
  <dd>{{productName}}</dd>
  <dt>Product Description:</dt>
  <dd>{{productDesc}}</dd>
  <dt>Product Price:</dt>
  <dd>${{productPrice}}</dd>
</dl>
<app-products *ngFor="let prod of products"
  [product]="prod"
  (prodDet)="showDetails($event)">
</app-products>

Component Communication Via Service

Open and edit "src/app/order.service.ts" then replace all Typescript with this.

import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class OrderService {

  // Observable string sources
  private createOrderSource = new Subject<string>();
  private orderConfirmedSource = new Subject<string>();

  // Observable string streams
  createOrder = this.createOrderSource.asObservable();
  orderConfirmed = this.orderConfirmedSource.asObservable();

  // Service message commands
  readyToOrder(item: string) {
    this.createOrderSource.next(item);
  }

  newOrder(customer: string) {
    this.orderConfirmedSource.next(customer);
  }
}

Open and edit "src/app/store/store.component.ts" then replace all Typescript codes with this.

import { Component } from '@angular/core';
import { OrderService } from '../order.service';

@Component({
  selector: 'app-store',
  templateUrl: './store.component.html',
  styleUrls: ['./store.component.css']
})
export class StoreComponent {

  items = ['iPhone X', 'Samsung S20', 'Asus ROG Phone 2'];
  history: string[] = [];
  orders = ['Buy iPhone X',
              'Buy Samsung S20',
              'Buy Asus ROG Phone 2'];
  nextOrder = 0;

  constructor(private orderService: OrderService) {
    orderService.orderConfirmed.subscribe(
      customer => {
        this.history.push(`${customer} confirmed new order`);
      });
  }

  orderReady() {
    const order = this.orders[this.nextOrder++];
    this.orderService.readyToOrder(order);
    this.history.push(`Order "${order}" ready`);
    if (this.nextOrder >= this.orders.length) { this.nextOrder = 0; }
  }

}

Open and edit "src/app/store/store.component.html" then replace all HTML tags with this.

<h1>Component Interaction with Service Example</h1>
<h2>Online Store</h2>
<button (click)="orderReady()">Order is Ready</button>
<app-customer *ngFor="let item of items"
  [item]="item">
</app-customer>
<h3>History</h3>
<ul>
  <li *ngFor="let event of history">{{event}}</li>
</ul>

Open and edit "src/app/customer/customer.component.ts" then replace all Typescript codes with this.

import { Component } from '@angular/core';
import { OrderService } from '../order.service';

@Component({
  selector: 'app-store',
  templateUrl: './store.component.html',
  styleUrls: ['./store.component.css']
})
export class StoreComponent {

  items = ['iPhone X', 'Samsung S20', 'Asus ROG Phone 2'];
  history: string[] = [];
  orders = ['Buy iPhone X',
              'Buy Samsung S20',
              'Buy Asus ROG Phone 2'];
  nextOrder = 0;

  constructor(private orderService: OrderService) {
    orderService.orderConfirmed.subscribe(
      customer => {
        this.history.push(`${customer} confirmed new order`);
      });
  }

  orderReady() {
    const order = this.orders[this.nextOrder++];
    this.orderService.readyToOrder(order);
    this.history.push(`Order "${order}" ready`);
    if (this.nextOrder >= this.orders.length) { this.nextOrder = 0; }
  }

}

Open and edit "src/app/customer/customer.component.html" then replace all HTML tags with this.

<h1>Component Interaction with Service Example</h1>
<h2>Online Store</h2>
<button (click)="orderReady()">Order is Ready</button>
<app-customer *ngFor="let item of items"
  [item]="item">
</app-customer>
<h3>History</h3>
<ul>
  <li *ngFor="let event of history">{{event}}</li>
</ul>


Dynamic Component Loader Example

In some cases, we need to load a component dynamically depend on the Application requirement in the runtime. For that, we will use Angular 9 ComponentFactoryResolver to add and load components dynamically. Angular comes with its own API for loading components dynamically.

For this example, we will create a dynamic discount splash that embeds the component. The first step is to create a directive that acts as the anchor point where to insert the components. Type this command to generate it.

ng g directive disc

Open and edit that file the replace all Typescript codes with this Angular ViewContainerRef module.

import { Directive, ViewContainerRef } from '@angular/core';

@Directive({
  selector: '[appDisc]'
})
export class DiscDirective {

  constructor(public viewContainerRef: ViewContainerRef) { }

}

Create an interface file "src/app/disc.component.ts" that specific component data type then adds these lines of Typescript codes.

export interface DiscComponent {
  data: any;
}

Create an Angular class "src/app/disc-item.ts" that only inject the Angular Type and data as any type then add these lines of Typescript file.

import { Type } from '@angular/core';

export class DiscItem {
  constructor(public component: Type<any>, public data: any) {}
}

Generate a new Angular Component that holds the dynamic component templates.

ng g component discount-splash

Open and edit "src/app/discount-splash/discount-splash.component.ts" file then replace all Typescript codes with this.

import { Component, Input, OnInit, ViewChild, ComponentFactoryResolver, OnDestroy } from '@angular/core';
import { DiscDirective } from '../disc.directive';
import { DiscItem } from '../disc-item';
import { DiscComponent } from '../disc.component';

@Component({
  selector: 'app-discount-splash',
  templateUrl: './discount-splash.component.html',
  styleUrls: ['./discount-splash.component.css']
})
export class DiscountSplashComponent implements OnInit, OnDestroy {

  @Input() items: DiscItem[];
  currentDiscIndex = -1;
  @ViewChild(DiscDirective, {static: true}) discHost: DiscDirective;
  interval: any;

  constructor(private componentFactoryResolver: ComponentFactoryResolver) { }

  ngOnInit() {
    this.loadComponent();
    this.getDiscs();
  }

  ngOnDestroy() {
    clearInterval(this.interval);
  }

  loadComponent() {
    this.currentDiscIndex = (this.currentDiscIndex + 1) % this.items.length;
    const discItem = this.items[this.currentDiscIndex];

    const componentFactory = this.componentFactoryResolver.resolveComponentFactory(discItem.component);

    const viewContainerRef = this.discHost.viewContainerRef;
    viewContainerRef.clear();

    const componentRef = viewContainerRef.createComponent(componentFactory);
    (componentRef.instance as DiscComponent).data = discItem.data;
  }

  getDiscs() {
    this.interval = setInterval(() => {
      this.loadComponent();
    }, 3000);
  }

}

Open and edit "src/app/discount-splash/discount-splash.component.html" file then replace all HTML tags with this.

<div class="discount-splash">
  <h3>Discount</h3>
  <ng-template appDisc></ng-template>
</div>

Generate components that will use as the templates for the discount splash.

ng g component product-disc
ng g component new-price

Open and edit "src/app/product-disc/product-disc.component.ts" then replace all Typescript codes with this.

import { Component, Input } from '@angular/core';
import { DiscComponent } from '../disc.component';

@Component({
  selector: 'app-product-disc',
  templateUrl: './product-disc.component.html',
  styleUrls: ['./product-disc.component.css']
})
export class ProductDiscComponent implements DiscComponent {

  @Input() data: any;

}

Open and edit "src/app/product-disc/product-disc.component.html" then replace all HTML tags with this.

<div class="product-disc">
  <h4>{{data.discTitle}}</h4>

  {{data.discBody}}
</div>

Give a little style by open and edit "src/app/product-disc/product-disc.component.css" then add these lines of CSS codes.

.product-disc {
  margin: 10px;
  padding: 20px;
  background-color: aqua;
}

Open and edit "src/app/new-price/new-price.component.ts" then replace all Typescript codes with this.

import { Component, Input } from '@angular/core';
import { DiscComponent } from '../disc.component';

@Component({
  selector: 'app-new-price',
  templateUrl: './new-price.component.html',
  styleUrls: ['./new-price.component.css']
})
export class NewPriceComponent implements DiscComponent {

  @Input() data: any;

}

Open and edit "src/app/new-price/new-price.component.html" then replace all HTML tags with this.

<div class="new-price">
  <h3>New Price</h3>
  <h4>{{data.item}}</h4>

  <p>{{data.oldPrice}}</p>

  <h4>{{data.newPrice}}</h4>
</div>

Give a little style by open and edit "src/app/new-price/new-price.component.css" then add these lines of CSS codes.

.new-price {
  margin: 10px;
  padding: 20px;
  background-color: lemonchiffon;
}

Next, generate a service that populates the data.

ng g service disc

Open that "src/app/disc.service.ts" then replace all Typescript codes with this.

import { Injectable } from '@angular/core';
import { ProductDiscComponent } from './product-disc/product-disc.component';
import { NewPriceComponent } from './new-price/new-price.component';
import { DiscItem } from './disc-item';

@Injectable({
  providedIn: 'root'
})
export class DiscService {

  getDiscs() {
    return [
      new DiscItem(ProductDiscComponent, {discTitle: 'Old iPhone Discount',
      discBody: 'Discount up to 60% for an old iPhone 6 to X series!'}),
      new DiscItem(ProductDiscComponent, {discTitle: 'Buy 3 Get 1', discBody: 'Buy 3 Smartphone get 1 Handphone'}),
      new DiscItem(NewPriceComponent, {item: 'Asus ROG Phone 2', oldPrice: 499, newPrice: 399}),
      new DiscItem(NewPriceComponent, {item: 'Asus ROG Phone 2', oldPrice: 499, newPrice: 399}),
    ];
  }
}

Next, open and edit "src/app/app.module.ts" then add this import.

import { DiscService } from './disc.service';

Add that import to the @NgModule providers.

  providers: [
    ...
    DiscService
  ],

Add the entryComponents in @NgModule array that contain these components.

entryComponents: [ ProductDiscComponent, NewPriceComponent ],

Next, we will show those dynamic components to the new component by generating it first.

ng g component dynamic

Open and edit "src/app/dynamic/dynamic.component.ts" then replace all Typescript codes with this.

import { Component, OnInit } from '@angular/core';
import { DiscService } from '../disc.service';
import { DiscItem } from '../disc-item';

@Component({
  selector: 'app-dynamic',
  templateUrl: './dynamic.component.html',
  styleUrls: ['./dynamic.component.css']
})
export class DynamicComponent implements OnInit {

  items: DiscItem[];

  constructor(private discService: DiscService) {}

  ngOnInit() {
    this.items = this.discService.getDiscs();
  }

}

Open and edit "src/app/dynamic/dynamic.component.html" then replace all HTML tags with this.

<h1>Dynamic Component Example</h1>
<div>
  <app-discount-splash [items]="items"></app-discount-splash>
</div>


Styling Component

As a default, the Angular 9 app is styled with standard CSS 3. But you can choose the CSS preprocessors like SASS, SCSS, or LESS. For the generated component, there is a separate file for CSS in the same folder with Typescript and template (HTML) files. If you are creating the component from scratch you can define separate CSS files or inside component metadata.

For example, we will create a Typescript file for the component "src/app/styled.component.ts" then add these lines of Typescript codes.

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

@Component({
  selector: 'app-styled',
  template: `
    <h1>Styled Component</h1>
    <div class="content">
      <p>The content all goes here.</p>
    </div>
  `,
  styles: [
    'h1 { font-weight: normal; }',
    '.content { background-color: lime, padding: 20px; margin: 10px; border: solid 1px silver; }'
  ]
})
export class StyledComponent {
/* . . . */
}

For an example of the style that loads the external CSS file, you can refer to the previous examples. One of them is "src/app/new-price/new-price.component.ts" and "src/app/new-price/new-price.component.css".

Also, you can put the styles inline with the template. Modify the previous example "src/app/styled.component.ts" with this.

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

@Component({
  selector: 'app-styled',
  template: `
    <style>
      h1 {
        font-weight: normal; 
      }
      .content {
        background-color: lime;
        padding: 20px;
        margin: 10px;
        border: solid 1px silver; 
      }
    </style>
    <h1>Styled Component</h1>
    <div class="content">
      <p>The content all goes here.</p>
    </div>
  `
})
export class StyledComponent {
/* . . . */
}

You can use CSS using <link> tags too. Here's the example of using Bootstrap CSS that load from the CDN.

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

@Component({
  selector: 'app-styled',
  template: `
    <link rel="stylesheet"
    href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
    integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u"
    crossorigin="anonymous">

    <div class="container">
      <div class="jumbotron">
        <h1>Styled Component</h1>
        <p>The content all goes here.</p>
      </div>
    </div>
  `
})
export class StyledComponent {
/* . . . */
}

Finally, we can wrap all Angular 9 components together in one page of this Angular 9 application by register the Component selector the "src/app/app.component.html".

<div class="container">
  <h1>Angular 9 Component Examples</h1>

  <div class="example">
    <app-item></app-item>
  </div>
  <div class="example">
    <app-sales></app-sales>
  </div>
  <div class="example">
    <app-ng-onchanges-example></app-ng-onchanges-example>
  </div>
  <div class="example">
    <app-ng-oninit-example></app-ng-oninit-example>
  </div>
  <div class="example">
    <app-product-details></app-product-details>
  </div>
  <div class="example">
    <app-store></app-store>
  </div>
  <div class="example">
    <app-dynamic></app-dynamic>
  </div>
</div>

That it's, the examples of the Angular 9 Components. You can find the full source codes from our GitHub.

If you don’t want to waste your time design your own front-end or your budget to spend by hiring a web designer then Angular Templates is the best place to go. So, speed up your front-end web development with premium Angular templates. Choose your template for your front-end project here.

That just the basic. If you need more deep learning about MEAN Stack, Angular, and Node.js, you can take the following cheap course:

Thanks!

Loading…