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 {}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.
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.
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.
<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.
<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 |
|---|---|
loading | The 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. |
error | The failure. Its title is announced once, politely. |
resolved, local | The empty slot when isEmpty(value), else the content. |
idle | The content or the empty slot, by isEmpty: nothing was asked for, so nothing loads. |
API
| Name | Description | Type | Default |
|---|---|---|---|
status | The state, when the app decides it. Ignored while resource is set. | WrLoadStateStatus | null | null |
resource | The resource to read the state from: resource(), rxResource() and httpResource() alike. The retry button calls its reload(). | Resource<T | undefined> | null | null |
isEmpty | Decides 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) => boolean | isEmptyValue |
lines | How many lines the default skeleton draws. | number | 3 |
errorLabel | The failure's title. Falls back to loadState.failed, then 'Could not load'. | string | null | null |
retryLabel | The retry button's label. Falls back to loadState.retry, then 'Try again'. | string | null | null |
emptyLabel | The default empty slot's title. Falls back to empty.noData through <wr-empty>. | string | null | null |
(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
| Name | Description | Type | Default |
|---|---|---|---|
[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-value | The 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-error | resource.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.
| Variable | Default | Declared on |
|---|---|---|
--wr-load-state-error-padding-x | var(--wr-space-lg) | .wr-load-state |
--wr-load-state-error-padding-y | var(--wr-space-xl) | .wr-load-state |
--wr-load-state-error-title-font-size | var(--wr-text-base) | .wr-load-state |
Dependencies
See also
Component SkeletonA placeholder block shown while content loads.Component ResultA full-size outcome screen, with ready-made 404, 403 and 500 pages.Component EmptyA placeholder for a view with no data.Component AlertAn inline status message.Util wrActionOne async write as signals: status, loading, value, error, a single-flight run().Component ButtonA button for an action or a link.Component ListA list of rows with leading, body and trailing slots.Component SpinnerAn inline loading indicator.