avatarFuji Nguyen

Summary

This context provides a tutorial on implementing a toast notification feature in an Angular application using Bootstrap 5 and an Angular service.

Abstract

The tutorial begins by explaining the concept of toasts in Angular applications and the use case of displaying a toast message when a user updates a position record. It then introduces a blog series that covers a full-stack Angular 15, Bootstrap 5, and .NET 7 API project. The main focus of the tutorial is a code snippet that demonstrates how to call an Angular service to display a toast using the @ng-bootstrap/ng-bootstrap library. The tutorial then provides step-by-step instructions on how to implement the toast feature, including creating a standalone component, wiring the toast component in the app.component.html file, creating a toast service, and calling the toast service from the desired component. The tutorial also provides source code examples for each step, including the toasts-container.component.ts, toasts-container.component.html, app.component.html, and the toast.service.ts files.

Bullet points

  • The tutorial provides a guide on implementing a toast notification feature in an Angular application using Bootstrap 5 and an Angular service.
  • The tutorial explains the concept of toasts in Angular applications and provides a use case of displaying a toast message when a user updates a position record.
  • The tutorial introduces a blog series that covers a full-stack Angular 15, Bootstrap 5, and .NET 7 API project.
  • The tutorial provides step-by-step instructions on how to implement the toast feature, including creating a standalone component, wiring the toast component in the app.component.html file, creating a toast service, and calling the toast service from the desired component.
  • The tutorial provides source code examples for each step, including the toasts-container.component.ts, toasts-container.component.html, app.component.html, and the toast.service.ts files.
  • The tutorial recommends using the AI service ZAI.chat, which provides the same performance and functions as ChatGPT Plus(GPT-4) but is more cost-effective.

Implement Toast with Bootstrap 5 in Angular 15 or 16 | Tutorial

Introduction

In the context of an Angular project, Toast typically refers to a notification or toast message library that is used to display temporary messages or notifications to the user. Toast messages are typically small pop-up boxes that appear on the screen to provide information or feedback to the user.

In this blog post, I will provide a code walkthrough on how to utilize an Angular service to display the toast component using Bootstrap 5. This service will enable passing data into the toast component for display purposes.

Use Case

I have a data entry form for a position record. When the user clicks the Update button in your form, I want to display a toast message to notify them that the position has been successfully updated. Please refer to the screenshot below for more details:

Tutorial Content

I have published a blog series titled Fullstack Angular 15, Bootstrap 5 & .NET 7 API: Project Demo and Tutorial. In this series, I provide a comprehensive guide along with a project demo. You have the option to download the complete full-stack source code and run it on your localhost environment.

In this blog post, I will specifically focus on a crucial code snippet that demonstrates how to call an Angular service and display a toast using the @ng-bootstrap/ng-bootstrap library. By following this code snippet, you will be able to grasp the implementation details and effectively utilize the toast service in your Angular application.

@ng-bootstrap/ng-bootstrap is a library that provides Angular directives and components to easily integrate and use Bootstrap's CSS and JavaScript components in Angular applications.

To implement the toast, follow these steps:

  1. Create the standalone component using ngb-toast component from the @ng-bootstrap/ng-bootstrap library.
  2. In the app.component.html file, wire the toast component by including its selector or placing it within the desired section of your application's template. This will ensure that the toast component is rendered and displayed as needed.
  3. Create a toast service that allows the calling of the toast component. The toast service should contain methods or functions that can be used to trigger and display the notification. You can implement the logic for displaying different types of toasts, such as success messages, error messages, or informational alerts, within this service.
  4. Finally, call the toast service from the component where you want to display the notification. Use the appropriate method or function provided by the toast service to show the desired toast message. You can pass parameters such as the message content, duration, and toast type to customize the notification according to your requirements.

By following these steps, you will be able to implement a toast functionality in your Angular application, allowing you to display informative and user-friendly notifications.

Part 1: Toast Component Source Code

An Angular component is a fundamental building block of Angular applications. It is a self-contained piece of code that encapsulates the data, behavior, and presentation logic of a part of the user interface (UI).

In the ng-bootstrap library, the component ngb-toast provides feedback messages as notifications to the user. Now, let’s assume you have a component where you want to display toast notifications. In the template of that component, you can use the ngb-toast component as follows as shown in the source code file toasts-container.component.html

<ngb-toast
  *ngFor="let toast of toastService.toasts"
  [class]="toast.classname"
  [header]="toast.header"
  [autohide]="true"
  [delay]="toast.delay || 5000"
  (hidden)="toastService.remove(toast)"
>
  <ng-template [ngIf]="isTemplate(toast)" [ngIfElse]="text">
    <ng-template [ngTemplateOutlet]="toast.textOrTpl"></ng-template>
  </ng-template>
<ng-template #text>{{ toast.textOrTpl }}</ng-template>
</ngb-toast>

In the above code, the component iterates over the toastService.toasts array and creates a toast for each item in the array. Let's go through the code and explain each part:

  • The <ngb-toast> component is used to display toast notifications. It is repeated using the *ngFor directive to iterate over the toastService.toasts array, creating a toast for each item.
  • The [class], [header], [autohide], and [delay] bindings are used to set various properties of the toast. The toast.classname binds to the class property, toast.header binds to the header property, and toast.delay sets the delay for the toast to automatically hide. If toast.delay is not provided, a default value of 5000 milliseconds (5 seconds) is used.
  • The (hidden) event binding listens for the toast being hidden or dismissed, triggering the toastService.remove(toast) method to remove the toast from the toastService.toasts array.
  • The first <ng-template> element uses [ngIf] and [ngIfElse] directives to conditionally render content based on whether the toast.textOrTpl property is a template or plain text. If it is a template, the [ngTemplateOutlet] directive is used to render the template. Otherwise, it falls back to the text template.
  • The second <ng-template> element with the #text reference variable is used as a fallback for plain text content. It displays the toast.textOrTpl value as text if it is not a template.

Here is the source code of the ToastsContainer in the source code file toasts-container.component.ts

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

import { ToastService } from '@app/services/toast/toast.service';
import { NgFor, NgIf, NgTemplateOutlet } from '@angular/common';
import { NgbToastModule } from '@ng-bootstrap/ng-bootstrap';

@Component({
  selector: 'app-toasts',
  standalone: true,
  imports: [NgbToastModule, NgIf, NgTemplateOutlet, NgFor],
  templateUrl: './toasts-container.component.html',
  host: { class: 'toast-container position-fixed top-0 end-0 p-3', style: 'z-index: 1200' },
})
export class ToastsContainer {
  constructor(public toastService: ToastService) {}

  isTemplate(toast: any) {
    return toast.textOrTpl instanceof TemplateRef;
  }
}

In the above code, we import the necessary dependencies, including Component and TemplateRef from @angular/core, and ToastService from a custom path. Then we define the ToastsContainer component using the @Component decorator. The component's selector is set to 'app-toasts', and the template file is specified with templateUrl. The host property is used to define the host element's attributes.

Inside the component class, we have a constructor that injects the ToastService instance into the component. The isTemplate() method is defined to check whether a toast's content is a template or plain text, based on the textOrTpl property.

For more information on how to use the ngb-toast, please visit online documentation.

Part 2: App Component Source Code

The app.component.html file in Angular is the template file associated with the root component of your Angular application, which is typically named AppComponent. It represents the HTML structure and content that will be rendered when the component is displayed.

The app.component.html file is where you define the layout, structure, and content of your application's root component. It typically contains HTML markup along with Angular template syntax, such as directives, data binding, and other Angular-specific syntax.

Here’s the source code in the source code file app.component.html.

<router-outlet></router-outlet>
<app-toasts aria-live="polite" aria-atomic="true"></app-toasts>

Let’s examine the code

  • <router-outlet></router-outlet>: The <router-outlet> is a placeholder directive provided by Angular for dynamically rendering components based on the current route. It is typically placed in the template file of the root component. The routed components will replace this placeholder based on the active route.
  • <app-toasts aria-live="polite" aria-atomic="true"></app-toasts>: The <app-toasts> component represents the custom ToastsContainer component in your application. It is responsible for displaying toast notifications. Here, it is included in the template to render the toast notifications. The aria-live attribute is set to "polite" to ensure that notifications are announced by screen readers without interrupting the current speech. The aria-atomic attribute is set to "true" to indicate that the entire region should be considered as a whole for accessibility purposes.

Part 3: Angular Toast Service Source Code

In Angular, a service is a class that is responsible for providing specific functionality and data to different parts of an application. Services act as a bridge between components, allowing them to share data, perform common tasks, and encapsulate business logic.

Here is the source code for the ToastService taken from the toaster.service.ts file:

import { Injectable, TemplateRef } from '@angular/core';

@Injectable({ providedIn: 'root' })
export class ToastService {
  toasts: any[] = [];

  show(header: string, textOrTpl: string | TemplateRef<any>, options: any = {}) {
    this.toasts.push({ header, textOrTpl, ...options });
  }

  remove(toast: any) {
    this.toasts = this.toasts.filter((t) => t !== toast);
  }

  clear() {
    this.toasts.splice(0, this.toasts.length);
  }
}

The code snippet is an Angular service called ToastService. This service is responsible for managing toast messages in an Angular application.

Explanation of the code:

  • The ToastService is decorated with @Injectable({ providedIn: 'root' }). This ensures that the service is available at the root level of your application and can be injected into other components.
  • The toasts property is an array that holds the toast messages. Each toast message is an object with properties like header, textOrTpl, and options.
  • The show() method is used to add a new toast message. It takes three parameters: header (a string representing the toast header), textOrTpl (a string or a TemplateRef representing the content of the toast message), and options (an optional object to provide additional options for the toast).
  • Inside the show() method, a new toast object is created by combining the provided parameters with the options object using the spread operator. The new toast is then added to the toasts array using the push() method.
  • The remove() method is used to remove a specific toast from the toasts array. It takes the toast object as a parameter and filters out that toast from the array.
  • The clear() method is used to remove all the toast messages from the toasts array. It achieves this by using the splice() method to remove all elements starting from index 0.

By using the ToastService in your Angular application, you can call the show() method to add toast messages, the remove() method to remove specific toasts, and the clear() method to remove all toasts. This service provides a convenient way to manage and display toast messages throughout your application.

Part 4: Position Detail Component Source Code (example of calling ToasterService to display Toaster Container)

In Angular, a component is a fundamental building block that encapsulates the functionality, data, and user interface (UI) of a specific part of a web application. Components are responsible for controlling and rendering a portion of the application’s user interface.

Here is the source code for the PositionDetailComponent taken from the position-detail.component.ts file:

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { UntypedFormGroup, UntypedFormBuilder, Validators } from '@angular/forms';
import { Logger } from '@app/core';
import { ApiHttpService } from '@app/services/api/api-http.service';
import { ApiEndpointsService } from '@app/services/api/api-endpoints.service';
import { Position } from '@shared/interfaces/position';
import { DataResponsePosition } from '@shared/interfaces/data-response-position';
import { ModalService } from '@app/services/modal/modal.service';

import { RxwebValidators } from '@rxweb/reactive-form-validators';
import { ToastService } from '@app/services/toast/toast.service';


const log = new Logger('Detail');

@Component({
  selector: 'app-detail',
  templateUrl: './position-detail.component.html',
  styleUrls: ['./position-detail.component.scss'],
})
export class PositionDetailComponent implements OnInit {
  formMode = 'New';
  sub: any;
  id: any;
  entryForm!: UntypedFormGroup;
  error: string | undefined;
  position!: Position;
  isAddNew: boolean = false;

  constructor(
    private toastService: ToastService,
    private route: ActivatedRoute,
    private formBuilder: UntypedFormBuilder,
    private apiHttpService: ApiHttpService,
    private apiEndpointsService: ApiEndpointsService,
    private modalService: ModalService,
  ) {
    this.createForm();
  }

  ngOnInit() {
    this.sub = this.route.params.subscribe((params) => {
      this.id = params['id'];
      if (this.id !== undefined) {
        this.read(this.route.snapshot.paramMap.get('id'));
        this.formMode = 'Edit';
      } else {
        this.isAddNew = true;
        this.formMode = 'New';
      }
    });
    log.debug('ngOnInit:', this.id);
  }

  // Handle Create button click
  onCreate() {
    this.create(this.entryForm.value);
    log.debug('OnInsert: ', this.entryForm.value);
    log.debug('OnInsert: ', this.entryForm.get('positionNumber')!.value);
  }

  // Handle Update button click
  onUpdate() {
    this.put(this.entryForm.get('id')!.value, this.entryForm.value);
    this.showToaster('Great job!', 'Data is updated');
  }

  // Handle Delete button click
  onDelete() {
    this.modalService
      .OpenConfirmDialog('Position deletion', 'Are you sure you want to delete?')
      .then((Yes) => {
        if (Yes) {
          this.delete(this.entryForm.get('id')!.value);
          log.debug('onDelete: ', this.entryForm.value);
        }
      })
      .catch(() => {
        log.debug('onDelete: ', 'Cancel');
      });
  }
  // CRUD > Read, map to REST/HTTP GET
  read(id: any): void {
    this.apiHttpService.get(this.apiEndpointsService.getPositionByIdEndpoint(id), id).subscribe(
      //Assign resp to class-level model object.
      (resp: DataResponsePosition) => {
        //Assign data to class-level model object.
        this.position = resp.data;
        //Populate reactive form controls with model object properties.
        this.entryForm.setValue({
          id: this.position.id,
          positionNumber: this.position.positionNumber,
          positionTitle: this.position.positionTitle,
          positionDescription: this.position.positionDescription,
          positionSalary: this.position.positionSalary,
        });
      },
      (error) => {
        log.debug(error);
      }
    );
  }
  // CRUD > Delete, map to REST/HTTP DELETE
  delete(id: any): void {
    this.apiHttpService.delete(this.apiEndpointsService.deletePositionByIdEndpoint(id), id).subscribe(
      (resp: any) => {
        log.debug(resp);
        this.showToaster('Great job!', 'Data is deleted');
        this.entryForm.reset();
        this.isAddNew = true;
      },
      (error) => {
        log.debug(error);
      }
    );
  }

  // CRUD > Create, map to REST/HTTP POST
  create(data: any): void {
    this.apiHttpService.post(this.apiEndpointsService.postPositionsEndpoint(), data).subscribe((resp: any) => {
      this.id = resp.data; //guid return in data
      this.showToaster('Great job!', 'Data is inserted');
      this.entryForm.reset();
    });
  }

  // CRUD > Update, map to REST/HTTP PUT
  put(id: string, data: any): void {
    this.apiHttpService.put(this.apiEndpointsService.putPositionsPagedEndpoint(id), data).subscribe((resp: any) => {
      this.id = resp.data; //guid return in data
    });
  }

  // reactive form
  private createForm() {
    this.entryForm = this.formBuilder.group({
      id: [''],
      positionNumber: ['', Validators.required],
      positionTitle: ['', Validators.required],
      positionDescription: ['', Validators.required],
      positionSalary: ['', RxwebValidators.numeric({ allowDecimal: true, isFormat: false })],
    });
  }

  // call modal service
  showToaster(title: string, message: string) {
    this.toastService.show(title, message, {
      classname: 'bg-success text-light',
      delay: 2000,
      autohide: true
    });
  }

}

The provided code is a TypeScript class PositionDetailComponent, which represents a component in an Angular application responsible for displaying the details of a position record.

Explanation of the code pertains to toast service:

  • The component imports necessary dependencies such as Component, OnInit, and various services, specifically the ToasterServcie
  • The delete() method sends a request to delete a position and shows a toast notification upon success.
  • The create() method sends a request to create a new position and shows a toast notification upon success.
  • The showToaster() method is responsible for displaying toast messages using the ToastService. It is called when certain actions are performed, such as successful updates or deletions.

This code provides functionality for managing position details, including CRUD operations and toast notifications.

Source Code

Recommended Contents

  1. What are Services in Angular?
  2. What is standalone component in Angular?
  3. Upgrading Angular 15 to 16: A Full-stack Web Development Project using Angular and .NetCore WebAPI
  4. Load Angular Component into Bootstrap Modal | Tutorial
  5. Fullstack Angular 15, Bootstrap 5 & NET 7 API: Project Demo and Tutorial
  6. Seven Object-Oriented Programming Jokes

Summary

The tutorial demonstrates how to create a toast in Angular using Bootstrap. It walks through the process of setting up the necessary components, services, and templates to achieve the desired functionality.

Angular
Bootstrap 5
Toast
Standalone Component
Recommended from ReadMedium