Building Angular Popup. Angular Alert Step-by-Step Guide
Building Angular Popup. Angular Alert Step-by-Step Guide

In this post we will build together Angular alert or Angular notification on the top of the page without any additional libraries.

Finished project

But if you just want to use a library and you don't want to implement anything I can highly recommend you to look on ng-bootstrap or angular-material libraries. There you get alert component which is closable, with styling and different configuration but in this post we want to create our own alert so we can fully control how it is working inside.

Architecture of the project

So what is the idea? In different places of our application we want to show our alert. Our alert is always situated at the same place on the top of the page. Which means we must render it in app.component and not in every single component.

Then we need something sharable which can call this alert and show it. Additionally after several seconds this alert must disappear.

This means that we need 2 different things. First of all we need an AlertComponent which will just have some trying and markup and secondly a service which will manage this alert.

Let's create first a simple alert component.

// src/app/alert/alert.component.ts
@Component({
  selector: 'alert',
  templateUrl: './alert.component.html',
  styleUrls: ['./alert.component.css'],
  standalone: true,
})
export class AlertComponent {}

And let's render just a word Alert inside our markup.

Now we need to render it on the top of the page in our app.component.

<!-- app/app.component.html !-->
<h1>Monsterlessons Academy</h1>

<alert></alert>

It order for it to work we must import our standalone AlertComponent in our AppComponent.

// src/app/app/app.component.ts
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
  standalone: true,
  imports: [RouterOutlet, AlertComponent],
})
export class AppComponent {}

Adding typings

Now in order to understand how our alert works and what we need to pass inside we want to create several types.

// src/app/alert/types/alertType.enum.ts
export enum AlertTypeEnum {
  success = 'success',
  info = 'info',
  warning = 'warning',
  danger = 'danger',
}

Here we defined all possible types of our alert. Now let's define an alert itself.

// src/app/alert/types/alert.interface.ts
import { AlertTypeEnum } from './alertType.enum';

export interface AlertInterface {
  type: AlertTypeEnum;
  text: string;
}

This is our alert which shows that in order to create an alert we must provide type and text inside.

Now let's define a an alert inside our component just for testing.

export class AlertComponent {
  alert: AlertInterface = {
    type: AlertTypeEnum.success,
    text: 'This is test alert'
  }
}

Adding markup

Now we can write markup for our testing alert.

<div [ngClass]="['alert', alert.type]" *ngIf="alert">{{ alert.text }}</div>

Here we render alert if it is not null (which happens when alert is not there) and apply 2 classes alert and alert.type which colors component differently.

But as we used ngClass and ngIf inside our markup we must import CommonModule to our alert component.

@Component({
  ...
  imports: [CommonModule],
})
export class AlertComponent {}

Creating CSS

The only thing that we are missing is CSS for our alert component. Let's add it now.

.alert {
  padding: 16px;
  border-radius: 6px;
  font-size: 16px;
  font-weight: 400;
}

.success {
  color: #0f5132;
  background: #d1e7dd;
}

.info {
  color: #055160;
  background: #cff4fc;
}

.warning {
  color: #664d03;
  background: #fff3cd;
}

.danger {
  color: #842029;
  background: #f8d7da;
}

Here is default styles for alert that we apply always and different types that we defined previously.

Finished project

As you can see in browser our testing alert is rendered and it gets correct colors.

Show alert from any place

But we don't want to just render alert always. We want to open it through service and provide different types inside. Let's create a service now.

// src/app/alert/services/alert.services.ts
@Injectable({
  providedIn: 'root',
})
export class AlertService {
  private alert$ = new Subject<AlertInterface>();

  setAlert(alert: AlertInterface): void {
    this.alert$.next(alert);
  }

  getAlert(): Observable<AlertInterface> {
    return this.alert$.asObservable();
  }
}

We create a Subject inside our AlertService so our AlertComponent can subscribe to it and understand when we want to show an alert. Also it allows us to pass new data to Subject which we do with setAlert method. As you can see we must provide an AlertInterface inside to change it.

We also have a getAlert method which return for us a Subject as an Observable. This is exactly how we will subscribe to changes in our AlertComponent.

Now let's try to trigger an alert from AppComponent.

<button (click)="showAlert(alertTypes.danger)">Show danger</button>
<button (click)="showAlert(alertTypes.success)">Show success</button>

Here I added 2 click events to show different types of alerts.

export class AppComponent {
  alertTypes = AlertTypeEnum;
  constructor(private alertService: AlertService) {}

  showAlert(type: AlertTypeEnum) {
    this.alertService.setAlert({
      type,
      text: 'THis is our test alert',
    });
  }
}

And here is showAlert function which just calls setAlert from our service. This is exactly how we trigger our alert from any place.

But we should also make some change in our AlertComponent so that we render alerts from the service.

export class AlertComponent implements OnInit {
  alert?: AlertInterface;

  constructor(private alertService: AlertService) {}

  ngOnInit(): void {
    this.alertService.getAlert().subscribe((alert) => {
      this.alert = alert;
    });
  }
}

Here we subscribed to our service and every time when we get the alert we render it in our markup.

Finished project

As you can see we are getting real alert and not our testing alert and render it.

Removing alert

But we are still missing a feature with removing our alert after several seconds from the screen.

export class AlertComponent implements OnInit {
  alert?: AlertInterface;
  timeoutId?: number;

  constructor(private alertService: AlertService) {}

  ngOnInit(): void {
    this.alertService.getAlert().subscribe((alert) => {
      this.alert = alert;
      this.resetTimer();
    });
  }

  resetTimer(): void {
    if (this.timeoutId) {
      window.clearTimeout(this.timeoutId);
    }
    this.timeoutId = window.setTimeout(() => {
      this.alert = undefined;
    }, 3000);
  }
}

Here are changes that we need to do to our AlertComponent. Every time when we get an alert we create setTimeout for 3 seconds. After 3 seconds we set alert back to undefined. We also clean old setTimeout every time when we get a new alert so we don't accidentally close our new alert.

Now our alert is fully ready.

And actually if you want to improve your Angular knowledge and prepare for the interview I highly recommend you to check my course Angular Interview Questions - Coding Interview 2023.

📚 Source code of what we've done