'Add dynamic loading messages with APP_INITIALIZER
I have an app which do some backend calls before initializing. I have a loading screen for this, which tells the user that something is currently loading. The problem is that I also want to show what is currently loading. If one of the backend calls fails, it should specifically displayed what went wrong. To illustrate the problem, I have programmed a small sample app.
app.module.ts
import { APP_INITIALIZER, NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { MatButtonModule } from '@angular/material/button';
import { MatMenuModule } from '@angular/material/menu';
import { MatTreeModule } from '@angular/material/tree';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { TreeNodeComponent } from './tree-node/tree-node.component';
import { ListElementComponent } from './list-element/list-element.component';
import { HttpClient } from '@angular/common/http';
import { forkJoin, switchMap, tap } from 'rxjs';
import { UserService } from './user.service';
import { ProudctService } from './proudct.service';
@NgModule({
declarations: [
AppComponent,
TreeNodeComponent,
ListElementComponent
],
imports: [
BrowserModule,
AppRoutingModule,
BrowserAnimationsModule,
MatButtonModule,
MatMenuModule,
MatTreeModule,
MatIconModule,
MatInputModule
],
providers: [{
provide: APP_INITIALIZER,
multi: true,
deps: [UserService, ProudctService],
useFactory: getUserConfig
}],
bootstrap: [AppComponent]
})
export class AppModule { }
export function getUserConfig(userService: UserService, productService: ProudctService) {
return function () {
switchMap(() => {
const user$ = userService.getUsers();
const product$ = productService.getAllProducts();
return forkJoin([user$, product$]);
})
}
}
index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>FocusManagerPlayground</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
</head>
<body class="mat-typography">
<app-root>
<div class="splash">Loading User... </div><div *ngIf="loading">Succesfull</div><div *ngIf="!loading">failed</div>
<div class="splash">Loading Product...</div>
</app-root>
<app-loading></app-loading>
</body>
</html>
and here a screenshot how it should looks like: If the first backend call was successful, on the right of the loading message should be a success icon anf if it fails, there should be a fail icon. As the same by loading the products.
Solution 1:[1]
Not sure if your example is what your doing or just meant to illustrate but is there a reason your not having the loading messages in the component? I would put your messages in a component and then separate the calls into a service that is kicked off on initialization. The component can reference the service and then you can use something like flags to update the template.
app.module.ts
export const userFactory = (provider: UserService): (() => Promise<void>) => () => provider.load();
@NgModule({
providers: [
UserService,
{ provide: APP_INITIALIZER, useFactory: userFactory, deps: [UserService], multi: true },
Example Service -- just doing the UserService as an example
@Injectable()
export class UserService {
usersLoading: boolean
usersLoadError: boolean
user: any;
load(): void {
this.usersLoading = true;
this.getData();
}
getData() {
setTimeout(() => {
this.user = { id: 1, name: 'hi' };
this.usersLoading = false;
this.usersLoadError = false;
}, 5000);
}
}
Template -- I don't really like flags but just using here to make a point you could also just check that the user property is truthy.
@Component({
selector: 'hello',
template: `<h1>Hello {{name}}!</h1>
{{userService.usersLoading ? 'Loading' : 'loaded' }}`,
styles: [`h1 { font-family: Lato; }`],
})
export class HelloComponent {
@Input() name: string;
constructor(public userService: UserService) {}
}
Please find a working example here:https://stackblitz.com/edit/angular-tj57g2?file=src/app/user.service.ts
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 |