Angular Complete Tutorial

Angular is a TypeScript-based free and open-source single-page web application framework. It is developed by Google. Angular is a web framework that empowers developers to build fast, reliable applications. Angular provides solid platform, tools, APIs, and libraries to simplify and streamline your development workflow. You need basic knowledge of OOP, Javascript, TypeScript, HTML & CSS to build an Angular application.

  1. Angular Components
  2. Angular Directives
  3. Angular Create Custom Directives
  4. @HostListener/User events in Directives.
  5. @Input/Passing values to directive/component.
  6. Passing more than one values.
  7. Angular Pipes
  8. Angular Forms
  9. Template-driven Forms
  10. Reactive Forms
  11. Form Group
  12. Nested Form or Form array
  13. Form Builder
  14. Form Validation
  15. Angular Component Lifecycle Hook Method
  16. ngOnChanges() ngOnInit() ngDoCheck() ngAfterContentInit() ngAfterContentChecked() ngAfterViewInit() ngAfterViewChecked() ngOnDestroy()
  17. Observable in Angular
  18. Angular Router
  19. Children Mapping in Angular Router Query Param in Angular Router Path Param in Angular Router

Angular Components

Component is the main block of an Angular Application. A Component contains the definition of the View and the data that defines how the View looks and behaves. The Angular Components are plain javascript classes and defined using. @component Decorator. This Decorator provides the component with the View to display & Metadata about the class.
The component contains selector, templateUrl, styleUrls etc.


  @Component({
    selector: 'selector-name', // selector for 
    templateUrl: './home.component.html', templateUrl for HTML template
    styleUrls: ['./home.component.scss'] // styleUrls for CSS
  })

Angular Directives

Angular provides many Built-in directives such as NgClass, NgStyle, NgModel, NgIf, NgFor, NgSwitch etc. There are two types of directives Attribute Directives and Structural Directives. NgClass, NgStyle, NgModel are the part of Attribute Directives and NgIf, NgFor, NgSwitch are the part of Structural Directives. Attribute directives let us change how things are rendered in the DOM. Structural directive lets us change DOM layout by adding or removing DOM elements.

Attribute Directives

Attribute directives provides the functionality of rendering the DOM. NgClass, NgStyle, NgModel are the Attribute Directives in Angular.

---Example of ngClass Attribute Directive---
<div [ngClass]="isHero ? 'hero' : ''">{{person.name}}</div>

<!--Example of ngStyle Attribute Directive-->
<div [ngStyle]="currentStyles">Example of ngStyle.</div>
    
    ---TS file---
    this.currentStyles = {
        'font-style': 'italic',
        'font-weight': 'bold',
        'font-size': '12px'
      };

<---Example of ngModel Attribute Directive--->
<input [(ngModel)]="currentItem.name" id="example-ngModel">

Structural Directives

Structural Directive allow to change DOM layout by adding or removing DOM elements. NgIf, NgFor, NgSwitch are the Structural Directives in Angular.

---Example of NgIf Structural Directive---
<div *ngIf="hero">{{hero.name}}</div>

---Example of ngFor Structural Directive---
<div *ngFor="let item of items">{{item.name}}</div>

---Example of ngSwitch Structural Directive---
<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>

Custom Directives In Angular

1. To create custom directive execute below command.

ng generate directive < directive name >
---Example of creating highlight directive---
PS E:\application\angular-mainapp\mainapp> ng generate directive highlight
CREATE src/app/highlight.directive.spec.ts (236 bytes)
CREATE src/app/highlight.directive.ts (147 bytes)
UPDATE src/app/app.module.ts (1452 bytes)
Angular CLI will be created a highlight.directive.ts file in the src/app folder. The corresponding test file will be created as src/app/highlight.directive.spec.ts, and declares the directive class in the AppModule.

2. Inject ElementRef in the Custom created directive. ElementRef has direct access to the host DOM element through its nativeElement property.

src/app/highlight.directive.ts

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

@Directive({
    selector: '[appHighlight]'
})
export class HighlightDirective {

    constructor(private el: ElementRef) { 
    this.el.nativeElement.style.backgroundColor = 'yellow';
    }

}
---In HTML Declare directive---
<p appHighlight>HighlightDirective Example Text!</p>

Handling user events in Directives. HostListener in Angular directive.

HostListener provides the functionality to capture of DOM event to listen and provides a handler method to run when that event occurs.

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

@Directive({
    selector: '[appHighlight]'
})
export class HighlightDirective {

  constructor(private el: ElementRef) {}

  @HostListener('mouseenter') onMouseEnter() {
    this.highlight('yellow');
  }

  @HostListener('mouseleave') onMouseLeave() {
    this.highlight('');
  }

  @HostListener('mouseenter') onMouseEnter() {
     this.highlight(this.appHighlight || 'red');
  }

  private highlight(color: string) {
    this.el.nativeElement.style.backgroundColor = color;
  }

}

Passing values into an attribute directive. @Input in Angular directive.

You need to inject >@Input in the directive to get value from component.

import { Directive , ElementRef, HostListener, Input} from '@angular/core';

    @Directive({
        selector: '[highLightColor]'
    })
    
    export class HighLightColor {
      @Input() highLightColor = '';
    
      constructor(private el: ElementRef) {}
    
      @HostListener('mouseenter') onMouseEnter() {
        this.highlight(this.highLightColor || 'yellow');
      }
    
      @HostListener('mouseleave') onMouseLeave() {
        this.highlight('');
      }
    
      private highlight(color: string) {
        this.el.nativeElement.style.backgroundColor = color;
      }
    
    }

---HTML Declaration---
    
<h5 [highLightColor]="'red'">H5</h5>
<h6 highLightColor>H6</h6>
    

Passing more than one values into an attribute directive

You need declare more than one @Input in the directive to get multiple values from component.

import { Directive , ElementRef, HostListener, Input} from '@angular/core';

    @Directive({
      selector: '[appHighlight]'
    })
    
    export class HighlightDirective {
      @Input() appHighlight = '';
      @Input() defaultColor = '';
    
      constructor(private el: ElementRef) {}
    
      @HostListener('mouseenter') onMouseEnter() {
        console.log(this.appHighlight+"--"+this.defaultColor);
        this.highlight(this.defaultColor || this.appHighlight || 'yellow');
      }
    
      @HostListener('mouseleave') onMouseLeave() {
        this.highlight('');
      }
    
      private highlight(color: string) {
        this.el.nativeElement.style.backgroundColor = color;
      }
    
    }

    HTML Declaration 
<h4 appHighlight defaultColor="blue">H4</h4>
<h5 [highLightColor]="'red'">H5</h5>
<h6 highLightColor>H6</h6>

Angular Pipes

Pipes are simple functions to use in template to accept an input value and return a transformed value. Angular provides many Built-in Pipes such as DatePipe, UpperCasePipe, LowerCasePipe, CurrencyPipe, DecimalPipe PercentPipe, AsyncPipe, JsonPipe etc.
To check the complete list of built-in pipes, see the pipes API documentation of Angular Pipes


To create a custom Pipe execute below command.
ng g p <pipe name>

example:
ng g p capital // capital is the pipe name. 

TS.file
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'capital'
})
export class CapitalPipe implements PipeTransform {

  transform(value: any): any {
    if (typeof value !== 'string') {
       return value;
     }
     return value.charAt(0).toUpperCase() + value.slice(1);
   }

}

Declare Pipes in HTML 
{{ 'tata' | capital }}

Output: Tata

Angular Forms

Form is the important part of application. You can submit data using Form. Angular provides two different approaches to handling user input through forms.

  1. Template-driven forms
  2. Reactive forms

Template-driven forms

If you have very basic form requirements and logic that can be managed by the Template-driven forms. It could be a good fit for basic form such as signup form with few fields. It rely on directives in the template to create and manipulate the underlying object model. Template-driven forms uses ngModel to bind data and it is asynchronous in data binding.

To use Template-driven forms need to use/inject ngModel inside the form and also need to inject NgModule, FormsModule in the App Module.

Component TS File Example of emplate-driven form
    
    import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core';
    import { NgForm } from '@angular/forms';
    
    @Component({
        selector: 'app-templateformtest',
        templateUrl: './templateformtest.component.html',
        styleUrls: ['./templateformtest.component.scss']
    })
    export class TemplateformtestComponent implements OnInit, OnDestroy{
    
        constructor() { }
    
        submitted = false;
    
        cities = ['Select City', 'Mumbai', 'Kolkata', 'Banglore', 'Delhi'];
    
        model = {
        name: "",
        emailId: "",
        mobileNumber:"",
        address:"",
        city: this.cities[0]
    
        }
    
        onSubmit() { 
        this.submitted = true; 
        }
    }



HTML File Example of Template-driven form
    
<div class="container">
<div [hidden]="submitted">
    <h1>Registration Form
    <form (ngSubmit)="onSubmit()" #registrationForm="ngForm">
    <div class="form-group">
        <label for="name">Name</label>
        <input type="text" class="form-control" id="name"
                required
                [(ngModel)]="model.name" name="name"
                #name="ngModel">
        <div [hidden]="name.valid || name.pristine" class="alert alert-danger">
        Name is required
        </div>{{name.valid}} -
    </div>

    <div class="form-group">
        <label for="emailId">Email ID</label>
        <input type="text" class="form-control" id="emailId"
                required
                [(ngModel)]="model.emailId" name="emailId" 
                #emailId="ngModel">
        <div [hidden]="emailId.valid || emailId.pristine" class="alert alert-danger">
            Email ID is required
        </div>
    </div>

    <div class="form-group">
        <label for="mobileNumber">Mobile Number</label>
        <input type="text" class="form-control" id="mobileNumber"
                required
                [(ngModel)]="model.mobileNumber" name="mobileNumber"
                #mobileNumber="ngModel">
        <div [hidden]="mobileNumber.valid || mobileNumber.pristine" class="alert alert-danger">
            Mobile Number is required
        </div>
    </div>

    <div class="form-group">
        <label for="address">Address</label>
        <input type="text" class="form-control" id="address"
            required
            [(ngModel)]="model.address" name="address"
            #address="ngModel">
        <div [hidden]="address.valid || address.pristine" class="alert alert-danger">
            Address is required
        </div>
    </div>

    <div class="form-group">
        <label for="city">City
        <select class="form-control" id="city"
                required
                [(ngModel)]="model.city" name="city"
                #city="ngModel">
        <option *ngFor="let city of cities" [value]="city">{{city}}</option>
        </select>
        <div [hidden]="city.valid || city.pristine" class="alert alert-danger">
        City is required
        </div>
    </div>
    
    <button type="submit" class="btn btn-success" [disabled]="!registrationForm.form.valid">Submit</button>
    </form>
</div>

<div [hidden]="!submitted">
    <h2>You submitted the following:</h2>
    <div class="row">
    <div class="col-xs-3">Name</div>
    <div class="col-xs-9">{{ model.name }}</div>
    </div>
    <div class="row">
    <div class="col-xs-3">Email ID</div>
    <div class="col-xs-9">{{ model.emailId }}</div>
    </div>
    <div class="row">
    <div class="col-xs-3">Mobile Number</div>
    <div class="col-xs-9">{{ model.mobileNumber }}</div>
    </div>
    
    <button type="button" class="btn btn-primary" (click)="submitted=false">Edit</button>
</div>
</div>


Reactive forms

Reactive forms are synchronous. The logic resides mainly in the typescript code. Compared to Template-driven forms, It is more robust, scalable, reusable, and testable. If forms are a key part of your application, or you're already using reactive patterns for building your application, use reactive forms.

Reactive forms use an explicit and immutable approach to managing the state of a form at a given point in time. Each change to the form state returns a new state, which maintains the integrity of the model between changes. Reactive forms are built around observable streams, where form inputs and values are provided as streams of input values, which can be accessed synchronously.

Reactive forms types

  1. Form group

  2. Form array oe Nested Form

  3. Form Builder

Form group

Form group provides the functionality of grouping of a fixed set of controls that you can manage together. All fields in a form will be collected within a group and a FormGroup instance provides its model value as an object.

Create a FormGroup

  1. Need to import ReactiveFormsModule in the app.module.ts.
  2. Need to import Component from '@angular/core' in the component.ts.
  3. Need to import FormGroup and FormControl from '@angular/forms' in the component.ts.

app.module.ts

    import { NgModule } from '@angular/core';
    import { BrowserModule } from '@angular/platform-browser';
    import { AppRoutingModule } from './app-routing.module';
    import { FormsModule, ReactiveFormsModule } from '@angular/forms';

    @NgModule({
    
        imports: [
        // other imports ...
        BrowserModule,
        AppRoutingModule,
        FormsModule,
        ReactiveFormsModule
        ],
    })
    export class AppModule {}

component.ts file 

    import { Component } from '@angular/core';
    import { FormGroup, FormControl } from '@angular/forms';
    
    @Component({
        selector: 'app-reactiveform-group',
        templateUrl: './reactiveform-group.component.html',
        styleUrls: ['./reactiveform-group.component.scss']
    })
    export class ReactiveformGroupComponent {
        userForm = new FormGroup({
        firstName: new FormControl(''),
        lastName: new FormControl(''),
        emailId: new FormControl(''),
        mobileNumber: new FormControl(''),
        address: new FormControl('')
        });
    
        onSubmit(){
        console.warn(this.userForm.value);
        }                                                  
    }
component.html file 
    
<form [formGroup]="userForm" (ngSubmit)="onSubmit()">
    <div>
        <label for="first-name">First Name: </label>
        <input id="first-name" type="text" formControlName="firstName">
    </div>
    <div>
        <label for="last-name">Last Name: </label>
        <input id="last-name" type="text" formControlName="lastName">
    </div>
    <div>
        <label for="last-name">Email ID: </label>
        <input id="last-name" type="text" formControlName="emailId">
    </div>
    <div>
        <label for="last-name">Mobile Number: </label>
        <input id="last-name" type="text" formControlName="mobileNumber">
    </div>
    <div>
        <label for="last-name">Address: </label>
        <input id="last-name" type="text" formControlName="address">
    </div>
    <div>
        <button type="submit" [disabled]="!userForm.valid">Submit</button>
    </div>
</form>
    

Form array or Nested Form

Form array provides the functionality of nested form. It is a dynamic form, where you can add and remove controls at run time.

Create a Nested Form

  1. Need to import ReactiveFormsModule in the app.module.ts.
  2. Need to import Component from '@angular/core' in the component.ts.
  3. Need to import FormGroup and FormControl from '@angular/forms' in the component.ts.

app.module.ts
    
    import { NgModule } from '@angular/core';
    import { BrowserModule } from '@angular/platform-browser';
    import { AppRoutingModule } from './app-routing.module';
    import { FormsModule, ReactiveFormsModule } from '@angular/forms';

    @NgModule({
    
        imports: [
        // other imports ...
        BrowserModule,
        AppRoutingModule,
        FormsModule,
        ReactiveFormsModule
        ],
    })
    export class AppModule {}

component.ts file 

    import { Component } from '@angular/core';
    import { FormControl, FormGroup } from '@angular/forms';

    @Component({
    selector: 'app-reactivenestedform',
    templateUrl: './reactivenestedform.component.html',
    styleUrls: ['./reactivenestedform.component.scss']
    })
    export class ReactivenestedformComponent {
    userForm = new FormGroup({
        firstName: new FormControl(''),
        lastName: new FormControl(''),
        emailId: new FormControl(''),
        mobileNumber: new FormControl(''),
        address: new FormGroup({
        street: new FormControl(''),
        city: new FormControl(''),
        state: new FormControl(''),
        zip: new FormControl(''),
        }),
    });

    onSubmit(){
        console.warn(this.userForm.value);
        console.warn(this.userForm.controls.address.value);
    }
  }
component.html file 
    
<form [formGroup]="userForm" (ngSubmit)="onSubmit()">
    <div>
        <label for="first-name">First Name: </label>
        <input id="first-name" type="text" formControlName="firstName">
    </div>
    <div>
        <label for="last-name">Last Name: </label>
        <input id="last-name" type="text" formControlName="lastName">
    </div>
    <div>
        <label for="last-name">Email ID: </label>
        <input id="last-name" type="text" formControlName="emailId">
    </div>
    <div>
        <label for="last-name">Mobile Number: </label>
        <input id="last-name" type="text" formControlName="mobileNumber">
    </div>
    <div formGroupName="address">
        <h2>Address
        <label for="street">Street: </label>
        <input id="street" type="text" formControlName="street">
        <label for="city">City: </label>
        <input id="city" type="text" formControlName="city">
        <label for="state">State: </label>
        <input id="state" type="text" formControlName="state">
        <label for="zip">Zip Code: </label>
        <input id="zip" type="text" formControlName="zip">
    </div>
    <div>
        <button type="submit" [disabled]="!userForm.valid">Submit</button>
    </div>
</form>
    

FormBuilder

FormBuilder API makes it easier to build reactive forms. Angular FormBuilder provides the functionality to add the FormGroup, nested FormGroup, FormArray & FormControls easily. It also allows us to set up the Validation rules for each of the controls.

Create a Nested Form

  1. Need to import ReactiveFormsModule in the app.module.ts.
  2. Need to import Component from '@angular/core' in the component.ts.
  3. Need to import FormBuilder and FormControl from '@angular/forms' in the component.ts.
  4. Need to initialize in constructorconstructor(private formBuilder: FormBuilder) {}

app.module.ts
    
    import { NgModule } from '@angular/core';
    import { BrowserModule } from '@angular/platform-browser';
    import { AppRoutingModule } from './app-routing.module';
    import { FormsModule, ReactiveFormsModule } from '@angular/forms';

    @NgModule({
    
        imports: [
        // other imports ...
        BrowserModule,
        AppRoutingModule,
        FormsModule,
        ReactiveFormsModule
        ],
    })
    export class AppModule {}

component.ts file 

    import { Component } from '@angular/core';
    import { FormBuilder, FormControl } from '@angular/forms';

    @Component({
    selector: 'app-form-builder-example',
    templateUrl: './form-builder-example.component.html',
    styleUrls: ['./form-builder-example.component.scss']
    })
    export class FormBuilderExampleComponent {
    constructor(private formBuilder: FormBuilder) {}

    userForm = this.formBuilder.group({
        firstName: new FormControl(''),
        lastName: new FormControl(''),
        emailId: new FormControl(''),
        mobileNumber: new FormControl(''),
        address: this.formBuilder.group({
            street: new FormControl(''),
            city: new FormControl(''),
            state: new FormControl(''),
            zip: new FormControl('')
        })
    });
    
  get firstname() {
    return this.userForm.get('firstname');
  }
 
  get lastname() {
    return this.userForm.get('lastname');
  }

  onSubmit(){
    console.log(this.userForm.value);
  }
}

component.html file 
    
<form [formGroup]="userForm" (ngSubmit)="onSubmit()">
    <div>
        <label for="first-name">First Name: </label>
        <input id="first-name" type="text" formControlName="firstName">
    </div>
    <div>
        <label for="last-name">Last Name: </label>
        <input id="last-name" type="text" formControlName="lastName">
    </div>
    <div>
        <label for="last-name">Email ID: </label>
        <input id="last-name" type="text" formControlName="emailId">
    </div>
    <div>
        <label for="last-name">Mobile Number: </label>
        <input id="last-name" type="text" formControlName="mobileNumber">
    </div>
    <div formGroupName="address">
        <h2>Address
        <label for="street">Street: </label>
        <input id="street" type="text" formControlName="street">
        <label for="city">City: </label>
        <input id="city" type="text" formControlName="city">
        <label for="state">State: </label>
        <input id="state" type="text" formControlName="state">
        <label for="zip">Zip Code: </label>
        <input id="zip" type="text" formControlName="zip">
    </div>
    <div>
        <button type="submit" [disabled]="!userForm.valid">Submit</button>
    </div>
</form>
    

Angular Form Validation

  1. Need to import ReactiveFormsModule in the app.module.ts.
  2. Need to import Component from '@angular/core' in the component.ts.
  3. Need to import Validators, FormBuilder and FormControl from '@angular/forms' in the component.ts.
  4. Need to initialize in constructorconstructor(private formBuilder: FormBuilder) {}

app.module.ts
    
    import { NgModule } from '@angular/core';
    import { BrowserModule } from '@angular/platform-browser';
    import { AppRoutingModule } from './app-routing.module';
    import { FormsModule, ReactiveFormsModule } from '@angular/forms';

    @NgModule({
    
        imports: [
        // other imports ...
        BrowserModule,
        AppRoutingModule,
        FormsModule,
        ReactiveFormsModule
        ],
    })
    export class AppModule {}

component.ts file 

import { Component } from '@angular/core';
import { FormBuilder, FormControl, Validators  } from '@angular/forms';

@Component({
selector: 'app-form-builder-example',
templateUrl: './form-builder-example.component.html',
styleUrls: ['./form-builder-example.component.scss']
})
export class FormBuilderExampleComponent {
    userForm : any;
    constructor(private formBuilder: FormBuilder) {
        this.userForm = this.formBuilder.group({
            firstname: ['', Validators.required],
            lastname: ['', [Validators.required, Validators.minLength(10)]],
            emailId: [Validators.required, Validators.email],
            mobileNumber: [Validators.required, Validators.minLength(10)],
            address: this.formBuilder.group({
                street: new FormControl(''),
                city: new FormControl(''),
                state: new FormControl(''),
                zip: new FormControl('')
            })
    });
} 
  
  onSubmit(){
    console.log(this.userForm.value);
    if (this.userForm.dirty && this.userForm.valid) {
      alert(
        `Name: ${this.userForm.value.name} Email: ${this.userForm.value.email}`
      );
    }
  
  }
}

component.html file 
    
    <div>
        <label for="firstname">First Name </label>
        <input type="text" id="firstname" name="firstname" formControlName="firstname">  
        <div class="alert alert-danger" *ngIf="firstname.touched && firstname.errors?.required">
            First Name Required.
        </div>
        
    </div>
     
    <div>
        <label for="lastname">Last Name </label>
        <input type="text" id="lastname" name="lastname" formControlName="lastname">
        <div class="alert alert-danger" *ngIf="lastname.touched && lastname.errors?.required">
            Last Name Required.
        </div>
        <div class="alert alert-danger" *ngIf="lastname.touched && lastname.errors?.minlength">
            Minimum 10 Character Required.
        </div>
    </div>

Angular Component Lifecycle / Lifecycle Hook Methods

Angular Lifecycle provides the functionality of input data change detection. Angular checks when data-bound properties change, and updates both the view and the component instance as needed.

Angular Components and Directives has same Lifecycle methods. Angular creates, updates, and destroys instances using lifecycle hook methods.

Lifecycle Hook Method
  1. ngOnChanges()
  2. ngOnInit()
  3. ngDoCheck()
  4. ngAfterContentInit()
  5. ngAfterContentChecked()
  6. ngAfterViewInit()
  7. ngAfterViewChecked()
  8. ngOnDestroy()

ngOnChanges()

The ngOnChanges() method runs whenever a parent component loads the child component or parent component change the @Input value of the child component. It runs before the ngOnInit(). If the component has @Input and @Input properties value change then Angular framework calls to the ngOnChanges() method.

This happens frequently, so any operation you perform here impacts performance significantly.

If component has no @Input or you use it without providing any @Input , the framework will not call to the ngOnChanges() method.

ngOnChanges() uses SimpleChanges as an argument.

---SimpleChanges Class---
class SimpleChanges{
    constructor(previousValue: any, currentValue: any, firstChange: boolean)
    previousValue: any;
    currentValue: any;
    firstChange: any;
    isFirstChane: any;
}

---Child Component TS file---
import { Component, Input, OnInit, OnChanges, SimpleChanges} from '@angular/core';
        
@Component({
    selector: 'app-onchanges',
    templateUrl: './onchanges.component.html',
    styleUrls: ['./onchanges.component.css']
})
export class OnChangesComponentExample implements OnInit, OnChanges {
    @Input() city = 'Mumbai';
    oldCity = 'Mumbai';
    diff = undefined;

    ngOnChanges(changes: SimpleChanges) {
        // This will only capture city changes as @Input uses
        console.log('Changes Detected. Is first change?', changes.rates.firstChange);
    }
}

---Child Component HTML---
City is {{city}}

---Parent Component TS file---
import { Component} from '@angular/core';
        
    @Component({
        selector: 'app-parent',
        templateUrl: './parent.component.html',
        styleUrls: ['./parent.component.css']
    })
    export class Parent implements{
        cityField = 'Mumbai';
    }

---Parent Component HTML---
<app-onchanges [city]=cityField>app-onchanges>
<input type="text" name="cityField" id="cityField" [(ngModel)]="cityField"/>

ngOnInit()

The ngOnInit() method called only once after the ngOnChanges() method. The purpose of the ngOnInit() method is to initialize the directive or component after Angular first displays the data-bound properties and sets the directive or component's input properties.

If you required to fetch data on the time of page loading, ngOnInit() is a good place for a component to fetch its initial data.

import { Component, OnInit } from '@angular/core';
        
@Component({
    selector: 'app-oninit',
    templateUrl: './oninit.component.html',
    styleUrls: ['./oninit.component.css']
})
export class OnInitComponentExample implements OnInit {

    ngOnInit() {
        // This method called only once 
        this.fetchDataFromDatabase();
    }
}

ngDoCheck()

The ngDoCheck() is called whenever any change detection occurs. Changes from either @Input or field value. whenever any change occurs, the ngDoCheck() method called immediately after ngOnChanges() and immediately after ngOnInit() on the first time.

The ngOnChanges() method called if we use @Input in the component or directive but ngDoCheck() is called whenever any change detection from either @Input or input field value.

import { Component, DoCheck, Input, OnChanges, SimpleChanges} from '@angular/core';
      
@Component({
    selector: 'app-docheck',
    templateUrl: './docheck.component.html',
    styleUrls: ['./docheck.component.css']
})
export class DoCheckCityComponent implements DoCheck, OnChanges {
    @Input() city = 'Mumbai';
    oldCity = 'Mumbai';
    diff = undefined;

    ngOnChanges(changes: SimpleChanges) {
        // This will only capture city changes as @Input uses
        console.log('Change detection. Is first change?', changes.rates.firstChange);
    }

    ngDoCheck() {
        // This will capture all changes (@Input or input fields both)
        if (this.city !== this.oldCity) {
            console.log("changes detected");
        }
    }
}

ngAfterContentInit()

The ngAfterContentInit() method runs only one time after the first ngDoCheck() method. This will not be executed after one time execution.

---Child Component TS file---
import { Component, DoCheck, Input, OnChanges, AfterContentInit, SimpleChanges} from '@angular/core';
  
@Component({
    selector: 'app-aftercontentinit',
    templateUrl: './aftercontentinit.component.html',
    styleUrls: ['./aftercontentinit.component.css']
})
export class AfterContentInitComponent implements DoCheck, OnChanges, AfterContentInit {
    @Input() city = 'Mumbai';
    oldCity = 'Mumbai';
    diff = undefined;

    ngOnChanges(changes: SimpleChanges) {
        // This will only capture city changes as @Input uses
        console.log('Is first change?', changes.rates.firstChange);
    }

    ngDoCheck() {
        // This will capture all changes (@Input or input fields both)
        if (this.city !== this.oldCity) {
            console.log("changes detected");
        }
    }

    ngAfterContentInit(){
        // This method runs once after the first ngDoCheck() method
        console.log("after content init");
    }
}

ngAfterContentChecked()

The ngAfterContentChecked() method executes every time when ngDoCheck() method called. This executes after ngDoCheck() method and the ngDoCheck() executes whenever any change detection occurs.

Use ngAfterContentChecked whenever you want to call a lifecycle event hook immediately after ngDoCheck.

---Child Component TS file---
import { Component, DoCheck, Input, OnChanges, AfterContentInit, AfterContentChecked , SimpleChanges} from '@angular/core';
  
@Component({
    selector: 'app-aftercontentchecked',
    templateUrl: './aftercontentchecked.component.html',
    styleUrls: ['./aftercontentchecked.component.css']
})
export class AfterContentCheckedComponent implements DoCheck, OnChanges, AfterContentInit, AfterContentChecked{
    
    @Input() city = 'Mumbai';
    oldCity = 'Mumbai';
    diff = undefined;

    ngOnChanges(changes: SimpleChanges) {
        // This will only capture city changes as @Input uses
        console.log('Is first change?', changes.rates.firstChange);
    }

    ngDoCheck() {
        // This will capture all changes (@Input or input fields both)
        if (this.city !== this.oldCity) {
            console.log("changes detected");
        }
    }

    ngAfterContentInit(){
        // This method runs once after the first ngDoCheck() method
        console.log("after content init");
    }

    ngAfterContentChecked(){
        // This method is called after every subsequent ngDoCheck
        console.log("after content checked")
      }
}

ngAfterViewInit()

The ngAfterViewInit() is called one time only after all child components are initialized and checked.

If you want to add functionality of after initialization of all child components and checked then ngAfterViewInit is useful.

---Child Component TS file---
import { Component, Input, AfterViewInit} from '@angular/core';

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

    @Input() city = 'Mumbai';
    oldCity = 'Mumbai';

    ngAfterViewInit(){
        // This method is called one time only after all child components are initialized and checked.
        console.log("after view init")
    }
}

ngAfterViewChecked()

The ngAfterViewChecked() is called each time of after execution of the ngAfterContentChecked() method.

If you want to add functionality of after initialization of all child components and checked then ngAfterViewChecked is useful.

---Child Component TS file---
import { Component, AfterViewChecked} from '@angular/core';

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

    @Input() city = 'Mumbai';
    oldCity = 'Mumbai';


    ngAfterViewChecked(){
        // This method is called each time of after execution of the ngAfterContentChecked method.
        console.log("after view Checked")
    }
}

ngOnDestroy()

The ngOnDestroy() method inside child component or directive is called whenever any child component remove/hide (using ngIf) from the parent component.

The lifecycle will be started again begin from ngOnInit() if you want to show again after removing the child component.

---Child component TS---
import { Component,  Input,  OnDestroy} from '@angular/core';

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

    @Input() city = 'Mumbai';

    ngOnInit() {
        // This method called only once 
        this.fetchDataFromDatabase();
    }
    
    ngOnDestroy(){
        // This method is called whenever child component remove/hide from parent component.
        console.log("On Destroy")
    }
}
--Child component HTML---
City is {{city}}

Parent component TS
import { Component} from '@angular/core';

    @Component({
        selector: 'app-parent',
        templateUrl: './appparent.component.html',
        styleUrls: ['./appparent.component.scss']
    })
    export class AppParentComponent implements {
    
        constructor() { }
        displayChild = true;

        toggleChildComponent(){
            this.displayChild = !this.displayChild;
        }
}
    

---Parent component HTML---
<app-destroy [city]=city *ngIf="displayChild"></app-destroy>
<button (click)="toggleChildComponent()">Show/Hide</button>

Observables in Angular

Observable provides the common asynchronous functionality in Angular such as HTTP module uses observables to handle AJAX requests/responses and Router and Forms modules use observables to listen for and respond to user-input events.

---Service TS---
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http'
import { Observable } from 'rxjs';

@Injectable({
    providedIn: 'root'
})
export class MessagesService {
  private AppUrl = 'https://jsonplaceholder.typicode.com/posts';
  private LanguageURL = "http://localhost:1111/language";
  private StudentURL = "http://localhost:1111/student";

  constructor(private http: HttpClient) {}

  getPostData(): Observable<String []>{
    return this.http.get<Employee[]>(this.AppUrl);
  }

  getLanguage(): Observable<Array>{
    return this.http.get<Array<string>>(this.LanguageURL);
  }

  getStudents(): Observable<String []>{
    return this.http.get<String []>(this.StudentURL);
  }
}


---Component TS---
import { Component } from '@angular/core';
import { MessagesService } from './services/messages.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
  providers: [MessagesService]
})
export class AppComponent implements OnInit{
    messages: string[] = [];
    serverData: Employee[] = [];
    languageData: Array<string> = [];

    constructor(private messageService: MessagesService){
        this.messages = messageService.getMessage();
    }

    this.messageService.getPostData().subscribe({
        next: (response) =>{
            this.serverData = response;
        },
        error: (error) => {
            console.error(error);
        }
    }); 
  }

  getLanguage(){
    this.messageService.getLanguage().subscribe({
      next: (response) =>{
        this.languageData = response;
      },
      error: (error) => {
        console.error(error);
      }
    });
  }
}

Angular Router

Angular Router service provides functionality of navigation from one view to another view. Need to import RouterModule to use the Router service in your Angular app.

---app-routing.module.ts---
import {  NgModule } from '@angular/core';
import {  RouterModule, Routes } from '@angular/router';
import {  HomeComponent } from './components/home.component';
import {  ContactUSComponent } from './components/contactUSComponent.component';
import {  ProfileComponent } from './components/profileComponent.component';
import {  UserSearchComponent } from './components/userSearchComponent.component';
import {  AuthComponent } from './components/AuthComponent.component';
import {  RegistrationComponent } from './components/RegistrationComponent.component';
import {  LoginComponent } from './components/LoginComponent.component';
import {  ChangePasswordComponent } from './components/ChangePasswordComponent.component';

const routes: Routes = [
    {path: "", component:HomeComponent},
    /*Browser URL - http://localhost:4291/ */
    {path: "home", component:HomeComponent},
    /*Browser URL - http://localhost:4291/home */
    {path: "contactus", component:ContactUSComponent},
    /*Browser URL - http://localhost:4291/contactus*/
    {path: "profile", component:ProfileComponent},
    /* HTML  <a [routerLink]="['profile']">User Details< /a>
        or < a [routerLink]="['profile']" [queryParams]="{ id:2 }">User Details< /a>
        Browser URL - http://localhost:4291/profile  or http://localhost:4291/profile?id=2  */
    {path: "usersearch", component:UserSearchComponent},
    /*Browser URL - http://localhost:4291/usersearch */
    {path: 'account/:id', component: AccountDetailComponent },
    {path: 'account/:id', component: AccountDetailComponent },
    /* < a [routerLink]="['/account', 123]">Account 123< /a>
        Browser URL - http://localhost:4291/account/123 */
    {path: "auth", component:AuthComponent, children: [
    /*Browser URL - http://localhost:4291/auth */      
        {path: "registration", component:RegistrationComponent},
        /*Browser URL - http://localhost:4291/auth/registration */
        {path: "login", component:LoginComponent},
        /*Browser URL - http://localhost:4291/auth/login */
        {path: "changepassword", component:ChangePasswordComponent}
        /*Browser URL - http://localhost:4291/auth/changepassword */
      }
    ];

@NgModule({
    imports: [RouterModule.forRoot(routes)],
    exports: [RouterModule]
})
export class AppRoutingModule { }
---HTML---

Browser URL - http://localhost:4291/
< a [routerLink]="">Dashboard< /a>
-----------------------------------

Browser URL - http://localhost:4291/home
< a [routerLink]="['home']">Home< /a>
-----------------------------------

Browser URL - http://localhost:4291/contactus
< a [routerLink]="['contactus']">Contact< /a>
-----------------------------------

Browser URL - http://localhost:4291/profile
< a [routerLink]="['profile']">Profile< /a>
-----------------------------------

Browser URL - http://localhost:4291/profile?id=2
< a [routerLink]="['profile']" [queryParams]="{ id:2 }">User Details for Id 2< /a>
-----------------------------------

Browser URL - http://localhost:4291/profile?id=3
< a [routerLink]="['profile']" [queryParams]="{ id:3 }">User Details for Id 3< /a>
-----------------------------------

Browser URL - http://localhost:4291/account/123
< a [routerLink]="['/account', 123]">Account 123< /a>
-----------------------------------

Browser URL - http://localhost:4291/account/150
< a [routerLink]="['/account', 150]">Account 150< /a>
---If want to navigate from TS file---
Browser URL - http://localhost:4291/home
this.router.navigate(
        ['/home']
);
or this.router.navigateByUrl('home');

-------------------------------

Browser URL - http://localhost:4291/profile
this.router.navigate(
    ['/profile']
); 
or this.router.navigateByUrl('profile');
-------------------------------  

Browser URL - http://localhost:4291/profile?id=2
this.router.navigate(
    ['/profile'],
    { queryParams: { id: 2'} }
);
or this.router.navigateByUrl('profile?id=2');
-------------------------------

Browser URL - http://localhost:4291/'profile?id=3
this.router.navigate(
    ['/profile'],
    { queryParams: { id: 3'} }
);
or this.router.navigateByUrl('profile?id=3');
-------------------------------

Browser URL - http://localhost:4291/account/3
this.router.navigate(
    ['/account','3'],
);
or this.router.navigateByUrl('account/3');