Presentation of information with loading and error scenarios

How can we effectively display data in an Angular view, considering loading state and error handling?

Imagine we are fetching a set of documents from our backend and need to present them in an Angular view. We want to address three possible scenarios by providing corresponding messages to the user:

  1. Loading your data...
  2. Error trying to retrieve your data!
  3. Data retrieved successfully!

Currently, my approach involves displaying both the "loading" and "error" messages briefly during the data retrieval process.

I am curious about how others tackle this common scenario. Is it feasible to manage this by manipulating the myDocs variable, or should I introduce another variable for better control?

Typescript Implementation (TS)

export class MyDocsComponent implements OnInit {

    myDocs: IMyDoc | null;

    ngOnInit() {

        // fetch myDocs
        this.myDocsService.getMyDocs()

        // if successful
        .then( myDocs => { this.myDocs = myDocs; })

        // if there's an error while retrieving myDocs from the service
        .catch( error => {
            // What actions should be taken here?
            // How can this be managed in the view 
            // to inform the user that data retrieval failed?
            // And also display a loading message prior to this.

            // Currently, I am using:
            this.myDocs = null;
            // However, this results in the error message 
            // being displayed prematurely during data loading.
        });
    }
}

HTML Markup (HTML)

<!-- Loading State -->
<div class="loading" *ngIf="!myDocs">
    Loading your data...
</div>

<!-- Error State -->
<div class="error" *ngIf="myDocs==null">
    Error trying to retrieve your data!                         
</div>

<!-- Success State -->
<div class="success" *ngIf="myDocs">
    Data retrieved successfully!
    <div class="list" *ngFor="let d of myDocs">{{ d.title }}</div>
</div>

Answer №1

Here is an example you can try out

file.component.ts

export class MyDocsComponent implements OnInit {

  myDocs: IMyDoc | null;

  state: 'loading' | 'loaded' | 'error' = 'loading';

  isLoading() {
    return this.state === 'loading';
  }

  isError() {
    return this.state === 'error';
  }

  isLoaded() {
    return this.state === 'loaded';
  }

  ngOnInit() {
    this.myDocsService.getMyDocs()
      .then(myDocs => {
        this.myDocs = myDocs;
        this.state = 'loaded';
      })
      .catch(error => {
        console.log(error);
        this.state = 'error';
      });
  }
}

file.component.html

<div *ngIf="isLoading"  class="loading">
  Loading your data...
</div>

<div *ngIf="isError" class="error">
  Error trying to retrieve your data!
</div>

<div *ngIf="isSuccess" class="success" >
  Data retrieved successfully!
  <div class="list" *ngFor="let d of myDocs"> {{ d.title }}
  </div>
</div>

Answer №2

Here is an alternative solution to address the issue at hand. In response to the OP's request, I have relocated it to a different answer. Please refrain from upvoting this.

This method involves leveraging some more advanced features in Angular such as

  • async pipe,
  • <ng-container>,
  • as keyword within the *ngIf statement,
  • else loading paired with <ng-template #loading>.

component.ts

export class MyDocsComponent implements OnInit {
  myDocs$: Promise<IMyDoc | string>;

  ngOnInit() {
    this.myDocs$ = this.myDocsService.getMyDocs()
      .then(response => {
        console.log('Data retrieved successfully');
        return response;
      })
      .catch(error => {
        console.log(error);
        return 'An error occurred';
      });
  }

  isError(resp: IMyDoc | string) {
    return typeof resp === 'string';
  }

  isSuccess(resp: IMyDoc | string) {
    return typeof resp !== 'string';
  }
}

component.html

<div *ngIf="myDocs$ | async as myDocs; else loading">
  <ng-container *ngIf="isSuccess(myDocs)">
    <div class="success"
      Data retrieved successfully!
      <div class="list" *ngFor="let d of myDocs"> {{ d.title }}
      </div>
    </div>
  </ng-container>

  <ng-container *ngIf="isError(myDocs)">
    <div class="error">
      Error retrieving data!
    </div>
  </ng-container>
</div>

<ng-template #loading>
  <div *ngIf="isLoading" class="loading">
    Loading data...
  </div>
</ng-template>

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

Tips for registering a provider in Angular 2 using dynamic data during runtime

Previously in Beta, I was able to achieve the following: export class AppBootstrapper { constructor(mySettings: AppSettings) { bootstrap(App, [ provide(AppSettings, { useFactory: () => mySettings }) ] } ...

After downloading the quickstart (angularjs2) from angular.io, I encountered an error when trying to start npm

[email protected] typing start G:\quickstart\quickstart-master tsc && concurrently "tsc -w" "lite-server" [1] 'lite-server' is not recognized as a valid command, [1] executable program or batch file. [1] lite-serve ...

All constructors at the base level must share a common return type

I am looking to convert my JSX code to TSX. I have a snippet that refactors a method from the react-bootstrap library: import {Panel} from 'react-bootstrap'; class CustomPanel extends Panel { constructor(props, context) { super(props ...

I'm encountering an issue where the npm install process is getting stuck while attempting to extract the xxxx package

When running the sudo npm install command on my local machine, everything works fine. However, once I pulled the code into an EC2 Ubuntu machine, the npm install process gets stuck at a certain point. The output shows "sill doParallel extract 1103" and I ...

The browser automatically adds a backslash escape character to a JavaScript object

When attempting to send an MQTT message to a topic from my angular app, the message needs to be in a specific syntax: { "Message": "hello" //the space after : is mandatory } However, upon sending the message in the correct format, the browser aut ...

Developing a search feature using Angular 6 with Observable subscription for the FrontEnd application

I have a unique challenge where I need to implement a full text search in the FrontEnd due to restrictions with the API. When the frontend starts up, it fetches all data entries from the Backend and subscribes them inside a component using an async pipe. T ...

After defining Partial<T>, encountering an error trying to access an undefined property is unexpected

In my function, I am attempting to standardize certain values by specifying the whole function type as Partial. However, despite declaring the interaction variable as Partial Type, I keep encountering the error message saying "Cannot read property endTime ...

String Compression - Number of Elements

Suppose I define a specific type: type SomeType = 'a' | 'b' | 'c' Is there a TypeScript function available that can calculate the number of unique values a variable of type SomeType can hold? assertEq(countUniqueValues(SomeTy ...

Creating mock objects with Jest

I am currently delving into the world of jest testing. Here is a snippet from an implementation class I'm working with: import { ExternalObject } from 'external-library'; export class MyClass { public createInstance(settings : ISettings) ...

Can you point me in the right direction for declaring the ImageObject format in Angular?

To create an Image Slider similar to the one shown here, I need to load images from a source. However, I'm unsure about where exactly in the file (possibly in the component.ts?) I should declare them: imageObject: Array<object> = [{ ...

Connecting the mat-progress bar to a specific project ID in a mat-table

In my Job Execution screen, there is a list of Jobs along with their status displayed. I am looking to implement an Indeterminate mat-progress bar that will be visible when a Job is executing, and it should disappear once the job status changes to stop or ...

Adapting Angular routes in real-time using observable state modifications

I am currently working on an Angular project that involves Angular routing. The code snippet below is extracted from my app-routing.module.ts file: // app-routing.module.ts: import { NgModule } from '@angular/core'; import { ActivatedRout ...

Designing a dynamic class object for a material table

I am in need of assistance with creating an HTML table using data retrieved from an API. The structure of the data is as follows: { strTableName: "TestTable", strComment:"Approved", lstTableHeaders:[ {strFieldName : "Sl No.", strType:" ...

Utilizing the axios create method: troubleshooting and best practices

I am attempting to use the axios library in my Next.js app (written in TypeScript) to access a public API for retrieving IP addresses from . In my index.ts file, I have the following code: import axios from "axios"; export const ipApi = axios.cr ...

What is the process for changing the text in a text box when the tab key on the keyboard is pressed in

When a user types a name in this text box, it should be converted to a specific pattern. For example, if the user types Text@1, I want to print $[Text@1] instead of Text@1$[Text@1]. I have tried using the keyboard tab button with e.keyCode===9 and [\t ...

Including the --aot flag in the Angular CLI can cause issues with the app

Recently, I encountered an issue with my Angular app while using dynamic forms. Everything was working fine until I added the --aot flag to my CLI command. Suddenly, I started receiving the error message "Property 'controls' does not exist on typ ...

solution for downloading large files from authenticated endpoint that works across multiple web browsers

I'm currently on the lookout for a solution to download large files (around 2 - 4 GB) from a bearer token protected api endpoint that is compatible with all common browsers including IE 11, Chrome, Firefox, Android Browsers, and Safari. I want it to w ...

TypeScript: Defining an Array Type within a Namespace or Module

Within a specific namespace, I have the following code: const operation1 = Symbol("operation1"); const operation2 = Symbol("operation2"); export interface Array<T> extends IConjable<T>, ISeqable<T> {} Array.prototype[op ...

Build a Node.js application using TypeScript and all necessary dependencies

I've developed a Node.js application using TypeScript and now I'm looking to compile it. Currently, only the source files in the src folder are included in the build. However, I also want to incorporate dependencies such as express, body-parser, ...

What are some secure methods for integrating iframes into an Angular 7 project?

Is there a secure method to set the URL for an iframe without bypassing security measures like using this.domsanitizer.bypassSecurityTrustResourceUrl(url)? While this approach may be flagged as a high vulnerability by tools such as Veracode, is there a w ...