NgxSuspense
This library is an experimetnal implementation of React Suspense for Angular.
Install the library
npm i ngx-suspense --save
Import the module:
imports: [NgxSuspenseModule];
Once you include the module, you will get following list of components you can use:
<SuspenseList revealOrder="together | forwards | backwards"></SuspenseList>
<Suspnese [fallback]="template" [bind]="suspneseServiceInstanse"></Suspnese>
and also you got one service:
NgxSuspenseService;
Usage
<Suspense>
Using [fallback]
with <ng-template></ng-template>
<Suspense [fallback]="tempalteRef"><YOUR_CONTENT_FROM_SERVER /></Suspense>
will use the template you passed in.
Using [bind]=suspenseServiceInstanse
, allow you to bind differnet service instanse to Suspense
component other than global one. Normally use [bind]
when you have multi Suspsnse
components inside one page.
@Component({
selector: "categories",
templateUrl: "./categories.component.html",
styleUrls: ["./categories.component.scss"],
providers: [NgxSuspenseService],
})
export class CategoriesComponent implements OnInit {
categories$: Observable<Category[]>;
constructor(
private categoriesService: CategoriesService,
private suspenseService: NgxSuspenseService
) {
this.categories$ = this.suspenseService.showingFor(
this.categoriesService.getCategories()
);
this.categories$ = this.categoriesService
.getCategories()
.pipe(this.suspenseService.showLoadingStatus());
}
}
<ng-template #tmp>
<loading-headline size="s"></loading-headline>
<div class="column">
<loading-headline size="m"></loading-headline>
<loading-text size="m"></loading-text>
<loading-text size="m"></loading-text>
</div>
</ng-template>
<main>
<section>
<Suspnese [fallback]="tmp">
<div *ngIf="categories$ | asnyc as categories"></div>
</Suspnese>
</section>
</main>
@Input() ariaLabel: string
Support for aria-label
, with default settings aria-busy=true
& aria-hidden=false
<SuspenseList>
Let's say you have two or more <Suspense>
inside one page.
Each of them resolve in different time, different orders, depends on network speed.
To avoid some part of UI jumping up & down, you can use <SuspenseList revealOrder="together">
as a parent component to wrap all <Suspense>
s. Then all <Suspense>
will resolve at the same time.
<SuspenseList revealOrder="together">
<Suspense [fallback]="tmp1" [bind]="suspenseService1">
<YOUR_COMPONENT1 [data]="data1$ | async" />
</Suspense>
<Suspense [fallback]="tmp2" [bind]="suspenseService2">
<YOUR_COMPONENT2 [data]="data2$ | async" />
</Suspense>
<Suspense [fallback]="tmp3" [bind]="suspenseService3">
<YOUR_COMPONENT3 [data]="data3$ | async" />
</Suspense>
</SuspenseList>
class YOUR_SMART_COMPONENT {
this.suspenseService1 = new NgxSuspenseService()
this.suspenseService2 = new NgxSuspenseService()
this.suspenseService3 = new NgxSuspenseService()
this.data1$ = this.apiService.loadData1()
.pipe(
this.suspenseService1.showLoadingStatus()
)
this.data2$ = this.apiService.loadData2()
.pipe(
this.suspenseService2.showLoadingStatus()
)
this.data3$ = this.apiService.loadData3()
.pipe(
this.suspenseService3.showLoadingStatus()
)
}
NgxSuspenseService
showingFor<T>(Obs$: Observable<T>): Observable<T>
You can pass in an observable which will finially complete, showingFor
will trigger the side effect which control loading spinner ON / OFF. Type friendly approach.
Example:
@Component({
selector: "categories",
templateUrl: "./categories.component.html",
styleUrls: ["./categories.component.scss"],
providers: [NgxSuspenseService],
})
export class CategoriesComponent implements OnInit {
categories$: Observable<Category[]>;
constructor(
private categoriesService: CategoriesService,
private suspenseService: NgxSuspenseService
) {
this.categories$ = this.suspenseService.showingFor(
this.categoriesService.getCategories()
);
}
}
showLoadingStatus()
The same effect with showingFor()
, just doesn't have type information.
this.categories$ = this.categoriesService
.getCategories()
.pipe(this.suspenseService.showLoadingStatus());
show() / hide()
If you wish to have normal control flow approach. You can use show / hide
this.suspenseService.show();
await this.apiService.load();
this.suspenseService.hide();
Configuration
You can set busyDelayMs
and busyMinDurationMs
.
imports: [
NgxSuspenseModule.forRoot({
busyDelayMs: 300,
busyMinDurationMs: 700,
}),
];