Example angular 13 component

Example angular 13 component

In this examples, we'll see angular 13 components for displaying a footer and header views of the application screen.

We will see both a basic angular 13 component that renders a simple template for the footer region of an application and an advanced component that injects an authentication service, access the template DOM and uses material design components.

We'll see how to subscribe to an observable from the the ngOnInit life-cycle method and other methods of the component and how to define and set the values of the component's properties.

We'll see how to use the takeUntil method of RxJS with a subject and the ngOnDestroy method of the component to unsubscribe from observables.

We'll see how to define component's properties and methods and bind them to the html template.

We'll see how to set the change detection strategy of a component to OnPush and how to manually trigger change detection in the component's class when properties that are bound to the template are set.

A component in angular is responsible for displaying a region of the user interface of the application that is associated with the component. An angular 13 application has one root component which is the main component that gets bootstraped when the application is started. It is also the parent component of every other component in the application which are together making a tree of components.

A component has three files:

  • The typescript file containing the typescript code for declaring the class decorated with the @Component decorator and encapsulating the business logic for the component;
  • The html template file containing the markup that will be rendered in the region of the screen where the component will be inserted using its selector or the angular router;
  • And the stylesheet file containing the styles for styling the html markup.

Generate a component with Angular CLI v13

You can use Angular CLI v13 to generate a basic component as follows:

ng g component footer

An example of an Angular 13 component for displaying a footer

This is an example of a basic angular 13 component that renders a simple footer. In the src/app/footer.component.ts file we have the following typescript code:

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

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

  constructor() { }

  ngOnInit(): void {
  }

}

The component implements the OnInit interface that provides the ngOnInit() lifecycle method that gets called when the component is initialized.

The component's decorator contains meta data about the component such as the selector that will be used to include the component in a template, and the urls of the associated template and stylesheets that will be used to render and style the component view.

In the src/app/footer.component.html file we have the following html code:

<p>
    ngApp (c) 2022
</p>

In the src/app/footer.component.css file we have the following css code:

p {
  margin: 1rem; 
}

An example of an Angular 13 header component with material design

This is an advanced example of an angular 13 component for displaying a header region of an application using material design.

You need to import angular material in the module containing this component. You also need to define an authentication service which provides the called methods and properties.

This component injects a bunch of custom and built-in services via constructor such as:

  • Router: for routing and navigation
  • Snackbar: for working the material desing snackbar component
  • AuthService: for authentication users. This is a custom service that we need to implement ourselves
  • ChangeDetectorRef for triggering change detection

In the src/app/header.component.ts file we have the following typescript code:

import { 
    Component, 
    OnInit, 
    ElementRef, 
    ViewChild, 
    OnDestroy, 
    ChangeDetectionStrategy, 
    ChangeDetectorRef } from '@angular/core';

import { Router } from '@angular/router';
import { User } from 'src/app/common/auth/user';
import { AuthService } from 'src/app/common/services/auth.service';
import { MatSnackBar } from '@angular/material/snack-bar';
import { takeUntil } from 'rxjs/operators';

@Component({
  selector: 'app-header',
  templateUrl: './header.component.html',
  styleUrls: ['./header.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class HeaderComponent implements OnInit, OnDestroy {
  @ViewChild('searchBar') searchBar: ElementRef | null = null;
  public signedIn: boolean = false;
  public authUser: User | null = null;
  private destroyNotifier$: Subject<boolean> = new Subject<boolean>();
  constructor(
    public authService: AuthService,
    private router: Router, 
    private snackBar: MatSnackBar, 
    private changeDetectorRef: ChangeDetectorRef) { }

  ngOnInit(): void {
    this.authService.authState
    .pipe(takeUntil(this.destroyNotifier$))
    .subscribe({
      next: (authState: AuthState) => {
        this.signedIn = authState.signedIn;
        this.authUser = authState.currentUser;
        this.changeDetectorRef.markForCheck();
      }
    });
  }
  ngOnDestroy(): void {
    this.destroyNotifier$.next(true);
    this.destroyNotifier$.complete();
  }
  logOut(): void {
    this.authService.logOut();
    this.router.navigateByUrl('/users/login');
  }
  findUsers(): void {
    const searchText = this.searchBar?.nativeElement.value;
    if(searchText == ''){
      this.snackBar.open('Enter a search text', 'Ok', {
        duration: 5 * 1000
      });
      return;
    }
    const result = this.authService.findUsers(searchText);
    console.log(result);
  }
}

First the component defines three public properties and one private attribute:

  • A public searchBar propety of type ElementRef and decorated with @ViewChild,
  • A public signedIn boolean property,
  • A public authUser property of type User, this is a custom class that need to be defined.
  • A private destroyNotifier$ Subject that can emit boolean values.

In the ngOnInit life-cycle method of the component, we subscribe to the authState property of the authentication service which exposes an observable that emits the auth state of the user.

We use the takeUntil operator, with the destroyNotifier notifying subject, to unsubscribe from the authState observable once the component is destroyed in the ngOnDestroy life-cycle method of the component that gets called when the component is being destroyed.

In the next method of the observer object passed to the subscribe() method we assign the signedIn and authUser properties of the component to their corresponding values in the authentication state returned by the authState observable. Then we run change detection by calling the markForCheck() method.

This last action is necessary because we used the changeDetection attribute in the component's decorator to tell angular to set the change detection strategy of the component to OnPush which means change detection will not run automatically, for performance reasons, until we call markForCheck() method. Otherwise, the changes will not be reflected in the component's template. In that case, the user interface will appear like if the user has not signed in successfully.

The next method is called when the the observable emits a value which is the authentication state in our example.

In the ngOnDestroy life-cycle method of the component, we emit a true value from the notifier subject that will instruct the takeUntil method to unsubscribe from the authentication state observable. Then we unsubscribe and complete the notifier subject itself.

In the logOut() method of the component, we call the logOut method of the authentication service and then we navigate to the /users/login route of the application. The component delegates the authentication functionality to the service.

We use the ViewChild decorator to access the element with the searchBar template variable in the component's template so that we can get the text which is searched for by users.

In the findUsers() method, we first check to see if the user entered an empty search string; if so, we call the snack bar component of material design to show a snack bar that tells the user to enter a search string. Otherwise, we delegate the action to the authentication service and show the results on the browser's console.

In the src/app/header.component.html file we have the following html markup:

<mat-toolbar>
    <mat-toolbar-row>
        <h1>ngApp v13</h1>
        <div *ngIf="signedIn">
            <input class="searchbar" type="text" placeholder="Search users..." #searchBar matInput>

            <button mat-icon-button (click)="findUsers()">
                <mat-icon>search</mat-icon>
            </button>
        </div>

        <span class="spacer"></span>
        <button *ngIf="signedIn" mat-icon-button routerLink="/home">
            <mat-icon aria-hidden="false" aria-label="Home">home</mat-icon>
        </button>
        <button *ngIf="!signedIn" mat-icon-button routerLink="/users/login">
            <mat-icon aria-hidden="false" aria-label="Log in">login</mat-icon>
        </button>
        <button *ngIf="signedIn" mat-icon-button (click)="logOut()">
            <mat-icon aria-hidden="false" aria-label="Log out">logout</mat-icon>
        </button>
    </mat-toolbar-row>
</mat-toolbar>

We create a material toolbar the contains the title of the application and a bunch of buttons and one input field. We the input field is referenced by the #searchBar template variable to access from the component's class using the ViewChild decorator.

The click event of search button is bound to the findUsers()) method the component which will be called when the user clicks on the button.

The html division containg the search bar and buttons are conditionally rendered using the ngIf directive only when the current user is signed in.

We also add two buttons, that are also conditionally rendered using the authentication state, that allow the user to navigate to the home and login pages of the application using the angular router via the routerLink directive.

Finally, we also add a conditionally rendered button that is rendered when the user is signed in and has the click event bound to the logOut() method of the component that simply signs out the user by delegating the action to the authentication service.

Finally, we can style the component in the associated src/app/header.component.css file as follows:

.spacer {
 flex: 1 1 auto;
}
button.mat-raised-button {
 border-radius: 0;
}
.searchbar {
 width: 99%;
 margin: 5px;
 height: 30px;
 border-radius: 23px;
}