Loading screen displayed while transitioning between routes within Angular

I have been struggling to implement a loading spinner in my project. How can I display a loading screen when changing routes in Angular?

Here is the HTML code snippet:

<div *ngIf="showLoadingIndicator" class="spinner"></div>

Below is the component.ts file content:

import { Component, OnInit } from '@angular/core';
import { Event, Router, NavigationStart, NavigationEnd,NavigationCancel,
  NavigationError } from '@angular/router';


@Component({
  selector: 'app-home-page',
  templateUrl: './home-page.component.html',
  styleUrls: ['./home-page.component.scss']
})
export class HomePageComponent implements OnInit {
  
  loading = true
  showLoadingIndicator = true;


  constructor(public router: Router) {
    this.router.events.subscribe((routerEvent: Event) => {
      if (routerEvent instanceof NavigationStart) {
        this.showLoadingIndicator = true
      }
      if (routerEvent instanceof NavigationEnd) {
        this.showLoadingIndicator = false
      }
      if (routerEvent instanceof NavigationError){
        this.showLoadingIndicator = false
      }
      if(routerEvent instanceof NavigationCancel){
        this.showLoadingIndicator = false
      }
    });

  }

This is the relevant CSS styling for the spinner:

.spinner {
  border :16px solid silver;
  border-top: 16px solid #337AB7;
  border-radius: 50%;
  width: 80px;
  height: 80px;
  animation: spin 700ms linear infinite;
  top: 50%;
  left: 50%;
  position: absolute;

}
@keyframes spin {
  0% {transform: rotate(0deg)}
  100%{transform:  rotate(-360deg)}
}

If anyone can provide insight into what might be causing the issue, I would greatly appreciate it. Thank you in advance.

Answer №1

Position the spinner at the same level as the Router outlet. You can have multiple spinners in your application, so the placement of the spinner will vary based on your specific use case.

In the class, create an observable with the router events, filter the stream to only emit start/termination events, and map them to a boolean value.

You can also add a side effect to display a label when tapping on the spinner, but this is optional. Observables and side effects are subject to lengthy discussions, but in this scenario, the side effects are minimal, leading to cleaner code without needing manual subscribe/unsubscribe manipulation of the observable.

CSS details have been left out for conciseness.

<theme-navbar>
</theme-navbar>
<loading-spinner
    *ngIf="loading | async; else routerOutlet"
    label="{{status}}"
    size="extra-large"
></loading-spinner>
<ng-template #routerOutlet>
    <router-outlet></router-outlet>
</ng-template>

The corresponding class would be:

import { Component, OnInit, inject } from '@angular/core';
import { NavigationCancel, NavigationEnd, NavigationError, NavigationStart, Router } from '@angular/router';
import { filter, map, tap } from 'rxjs';

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.scss'],
})
export class AppComponent implements OnInit {
    
    status = '';

    loading = inject(Router).events.pipe(
        filter((e) =>[ // only react to start and finish events
            NavigationStart,
            NavigationEnd,
            NavigationError,
            NavigationCancel
        ].some((constructor) => e instanceof constructor)),
        map((e) => e instanceof NavigationStart),
        // this line saves us to have to manually subscribe to the stream
        tap(e=>{this.status = e ? 'navigating' : 'done'})
    );
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Implementing Bootstrap 5 JS within an Angular 11 component TypeScript

I am currently working on a project that utilizes Angular 11 and we are aiming to integrate Bootstrap 5 native JS without relying on third-party libraries like ng-bootstrap, MDB, or ngx-bootstrap (jQuery is not being used as well). I understand that using ...

I am attempting to incorporate a List View within a Scroll View, but they are simply not cooperating. My goal is to display a collection of items with additional text placed at the bottom

This is how it should appear: item item item item additional text here I am trying to create a layout where the list is in List View for benefits like virtual scrolling, but the entire layout needs to be within a Scroll View. I want to be able to con ...

Activate the capture property for the file selection based on the label that is selected

This is a form that consists of two labels: <form method="POST" action='/process' enctype="multipart/form-data"> <div> <label for="file" class="upload-button"><i class=" ...

Uniform retrieval function for interaction with API

I have developed my server backend using PHP and now I am looking to establish communication between the frontend (typescript) and backend. For each of my API requests, I desire to receive a standardized response. Hence, every response from the server fol ...

The Angular application's navbar component is not receiving the necessary bootstrap classes

I am currently using Bootstrap 3.4.1 with AngularCLI 6.0.8, but I am facing an issue where none of the identified Bootstrap classes are showing up on my webpage when I run ng serve. Despite trying to update Bootstrap versions and experimenting with new cl ...

Resolved error: Angular 'title' identifier is undefined. The 'Movie[]' array does not have this member

Attempting to fetch data from the backend using Angular services. After reading the Angular docs, it was mentioned that interfaces should be used when sending requests somewhere It should look something like this: return this.http.get<Movie[]>(th ...

Using the "unicode-range" property for numerical values

Having incorporated a custom font using @font-face, I am attempting to showcase text in two different languages on a webpage with distinct fonts utilizing the font-face at rule in CSS and employing the unicode-range property. While the text appears corre ...

I am attempting to retrieve custom cellRendererParams within the CustomCellRenderer class

I'm currently working with Ag-Grid in my angular application and am trying to implement a custom cell renderer. The tutorial I followed uses ICellRendererParams for the parameter type passed to the init event. agInit(params: ICellRendererParams): void ...

Angular TextInput Components don't seem to function properly when dealing with arrays

I am trying to create a collection of text input components with values stored in an array. However, when using the following code, the values seem to be placed incorrectly in the array and I cannot identify the bug. <table> <tr *ngFor="let opt ...

What is the process for implementing a CSS theme switch in a CHM file and ensuring that it remains persistent?

Welcome to my tech world Greetings! I am a tech writer with a side interest in scripting JavaScript/jQuery for our help file (CHM file). While I consider myself a beginner in JS scripting, I have ventured into the realm of dynamic theme swapping within CH ...

The incorrect initial state is causing issues in the Zustand state management on the Next.js server side

While utilizing zustand as a global state manager, I encountered an issue where the persisted states were not being logged correctly in the server side of nextjs pages. The log would only show the default values (which are null) and not the updated state v ...

"Choose one specific type in Typescript, there are no in-b

Need help returning an object as a fetch response with either the property "data" or "mes": { data: Data } | { mes: ErrMessage } Having trouble with TypeScript complaining about this object, let's call it props: if (prop.mes) return // Property &a ...

Verify that the Angular service has been properly initialized

I am currently testing my Angular service using Karma-Jasmine and I need to verify that the loadApp function is called after the service has been initialized. What would be the most effective approach for testing this? import { Injectable, NgZone } from ...

Effortless implementation of list loading with images and text in the Ionic 2 framework

Can someone provide guidance on creating a lazy loading list with both images and text? I understand that each image in the list will require a separate http request to download from the server. Should caching be implemented for these image downloads? Addi ...

Finding a way to reference multiple components within a mapping function

In order to set a ref to each project within the map function, I am trying to pass forwardRef from child to parent. At the moment, I am only able to get a single Project. However, I need to set refs to an array list so I can work with it. Below is what I h ...

An improved method for implementing conditional statements in Angular

After researching online, I came across some discussions about using if else logic in Angular. Although I was able to achieve the desired outcome, I am curious if there is a more efficient or alternative way to implement if else statements in Angular. In ...

Using a carousel component in Bootstrap

Just starting out with this, trying to customize Bootstrap to change slides automatically. I followed the documentation at https://getbootstrap.com/docs/4.3/components/carousel/ but for some reason, the slides aren't changing on an interval, even thou ...

Interactive loadChild components

I've been attempting to dynamically import routes from a configuration file using the following code snippet: export function buildRoutes(options: any, router: Router, roles: string[]): Routes { const lazyRoutes: Routes = Object.keys(options) ...

What is the process for generating a dynamic array in typescript?

Looking to create a TypeScript dynamic array with the desired format: const display = [ { id: 1, displayName: "Abc1" }, { id: 2, displayName: "Abc2" }, { id: 3, displayName: "Abc3" } ] Attempted the following code ...

Can a TypeScript-typed wrapper for localStorage be created to handle mapped return values effectively?

Is it feasible to create a TypeScript wrapper for localStorage with a schema that outlines all the possible values stored in localStorage? Specifically, I am struggling to define the return type so that it corresponds to the appropriate type specified in t ...