ComponentAdded in v15.6

Load State

One wrapper for a screen that loads: the skeleton, the failure with a retry, the empty slot or the content.

Installation

Import the class, and @use the entry point in your global styles. Nothing warns you when the @use is missing.

import { Component } from '@angular/core';
import { WrLoadState, WrLoadStateContent, WrLoadStateEmpty, WrLoadStateError, WrLoadStateLoading } from 'ngwr/load-state';

@Component({ imports: [WrLoadState, WrLoadStateContent, WrLoadStateEmpty, WrLoadStateError, WrLoadStateLoading] })
export class MyComponent {}

Basic usage

Give it a resource and a content template. The content is created once the value is ready; a reload keeps it on screen and marks the wrapper busy.

ReloadReload empty Reload and fail
    #1042 $128.00 #1043 $54.20 #1044 $310.75
import { httpResource } from '@angular/common/http';

@Component({
  imports: [WrLoadState, WrLoadStateContent, WrList, WrListItem],
  template: `
    <wr-load-state [resource]="orders">
      <ng-template wrLoadStateContent [wrLoadStateContentOf]="orders" let-orders>
        <wr-list>
          @for (order of orders; track order.id) {
            <wr-list-item>{{ order.id }}</wr-list-item>
          }
        </wr-list>
      </ng-template>
    </wr-load-state>
  `,
})
export class Orders {
  readonly orders = httpResource<Order[]>(() => '/api/orders');
}

Status

Without a resource the app sets status. The retry button emits (retry); answer it by loading again.

    Loaded.
<wr-load-state [status]="status()" (retry)="load()">
  <ng-template wrLoadStateContent>Loaded.</ng-template>
</wr-load-state>

Templates

Each part is a template: wrLoadStateLoading, wrLoadStateError and wrLoadStateEmpty replace the defaults. The error template gets the error, retry() and retrying.

Could not load orders

Gateway timeout

Try again
Could not load
<wr-load-state [resource]="orders">
  <ng-template wrLoadStateLoading><wr-spinner /></ng-template>
  <ng-template wrLoadStateError let-error let-retry="retry" let-retrying="retrying">
    <wr-alert type="danger" title="Could not load orders" [message]="error.message">
      <wr-btn wrAlertActions size="sm" [loading]="retrying" (click)="retry()">Try again</wr-btn>
    </wr-alert>
  </ng-template>
  <ng-template wrLoadStateEmpty>
    <wr-empty title="No orders yet"><wr-btn wrEmptyActions size="sm">Create one</wr-btn></wr-empty>
  </ng-template>
  <ng-template wrLoadStateContent [wrLoadStateContentOf]="orders" let-orders>...</ng-template>
</wr-load-state>

What is empty

isEmpty decides between the empty slot and the content. The default is null, undefined and an empty array.

<wr-load-state [resource]="page" [isEmpty]="v => !v || v.items.length === 0">

States

What each resource status draws.

resource.status()Drawn
loadingThe skeleton.
reloading What was on screen, marked busy. After a failed load, the failure with its button busy. The skeleton when there is no value yet.
errorThe failure. Its title is announced once, politely.
resolved, localThe empty slot when isEmpty(value), else the content.
idleThe content or the empty slot, by isEmpty: nothing was asked for, so nothing loads.

API

NameDescriptionTypeDefault
statusThe state, when the app decides it. Ignored while resource is set.WrLoadStateStatus | nullnull
resourceThe resource to read the state from: resource(), rxResource() and httpResource() alike. The retry button calls its reload().Resource<T | undefined> | nullnull
isEmptyDecides whether a settled value shows the empty slot instead of the content. The default is empty for null, undefined and an empty array.(value: T | undefined) => booleanisEmptyValue
linesHow many lines the default skeleton draws.number3
errorLabelThe failure's title. Falls back to loadState.failed, then 'Could not load'.string | nullnull
retryLabelThe retry button's label. Falls back to loadState.retry, then 'Try again'.string | nullnull
emptyLabelThe default empty slot's title. Falls back to empty.noData through <wr-empty>.string | nullnull
(retry)Emitted when the retry button is pressed, once per press that starts a retry. With a resource, reload() has been called as well.void—

Templates API

NameDescriptionTypeDefault
[wrLoadStateContent]On an <ng-template>: the content, created once ready. Import WrLoadStateContent.directive-
[wrLoadStateContentOf]The resource, for the type checker only: types let-value. Bind the same resource as [resource].Resource<T | undefined> | null | undefined—
let-valueThe resource's value. undefined when driven by status.T-
let-reloading="reloading"A reload is in flight behind the content.boolean-
[wrLoadStateLoading]On an <ng-template>: replaces the default skeleton. Import WrLoadStateLoading.directive-
[wrLoadStateError]On an <ng-template>: replaces the default failure. Import WrLoadStateError.directive-
let-errorresource.error(). undefined when driven by status.Error | undefined-
let-retry="retry"Reloads the resource and emits retry. Does nothing while one is in flight.() => void-
let-retrying="retrying"A retry is in flight.boolean-
[wrLoadStateEmpty]On an <ng-template>: replaces the default <wr-empty>. Import WrLoadStateEmpty.directive-

CSS variables

Set these on the element itself, with a rule more specific than its Declared on selector. In a global stylesheet, put a class of yours on an ancestor it really has: <body> for an overlay panel, which renders outside your markup. In a component stylesheet the selector alone works, but only for an element in that component's own template. A value on a wrapper or :root is shadowed, because ngwr/load-state declares each default on its own element.

VariableDefaultDeclared on
--wr-load-state-error-padding-xvar(--wr-space-lg).wr-load-state
--wr-load-state-error-padding-yvar(--wr-space-xl).wr-load-state
--wr-load-state-error-title-font-sizevar(--wr-text-base).wr-load-state

Dependencies

See also