0.0.4 • Published 4 years ago

ngx-suspense v0.0.4

Weekly downloads
5
License
MIT
Repository
-
Last release
4 years ago

NgxSuspense

This library is an experimental implementation of React Suspense for Angular.

DEMO

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
  ) {
    // Type safe
    this.categories$ = this.suspenseService.showingFor(
      this.categoriesService.getCategories()
    );

    // or
    // Side effect
    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">
      <!-- Your content to be loaded below -->
      <div *ngIf="categories$ | asnyc as categories"></div>
    </Suspnese>
  </section>
</main>

In the example uses ngx-loading-skeleton for showing loading shimmer

@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, // within 300ms, don't show the loading skeleton; default value: 0
    busyMinDurationMs: 700, // showing loading skeleton for at least 700ms; default value: 0
  }),
];