CngxAsyncClick
projects/common/interactive/async-click/async-click.directive.ts
Import#
import { CngxAsyncClick } from '@cngx/common/interactive'
Description#
Async action handler with loading state, auto-disable, and success/error feedback.
Place on any clickable element (button, link, div). Executes the provided
async action on click, tracks the full lifecycle as a state machine, and
guards against double-clicks. While pending it communicates busy via
aria-busy + aria-disabled and swallows clicks WITHOUT the hard
disabled attribute, so keyboard focus stays on the control instead of
dropping to <body> mid-action. Note: the guard prevents the ACTION from
re-running and calls preventDefault(), but a consumer (click) handler
on the same element still fires while pending - gate it on pending()
when it must not. Success and failure are announced through
an auto-rendered polite live region (sibling of the host - inside a
<button> the text would pollute the accessible name); opt out with
[autoAnnounce]="false" and bind announcement to your own region.
Basic usage
<button [cngxAsyncClick]="saveAction" #btn="cngxAsyncClick">
@switch (btn.status()) {
@case ('pending') { Saving... }
@case ('success') { Saved! }
@case ('error') { Failed }
@default { Save }
}
</button>With Material
<button mat-raised-button [cngxAsyncClick]="submitForm" #btn="cngxAsyncClick">
@if (btn.pending()) { <mat-spinner diameter="20" /> Submitting... }
@else { Submit }
</button>On any element
<a role="button" [cngxAsyncClick]="navigate" #btn="cngxAsyncClick">Go</a>Metadata#
Host#
Relationships
Index#
Inputs#
AsyncActionThe async action to execute on click.
{ alias: 'cngxAsyncClick' }Auto-render the polite live region announcing success/failure.
Opt out (false) to wire announcement to your own region -
also when a toast/alert bridge (cngxToastOn, cngxAlertOn) on the
same element already announces the settle, or the user hears it twice.
trueExternal busy override. When true, the element is aria-busy even though
this directive is not running its own action - for a wrapper that tracks an
operation outside the click (e.g. an [externalState]). Purely an ARIA
hint: it does not block clicks (gate those with [enabled]).
falseWhen false, clicks are ignored and the element is marked aria-disabled
(never the hard disabled attribute, so focus survives). Communicates the
"why" to assistive tech instead of silently swallowing the click.
trueLabel announced to screen readers on failure.
'Action failed'Instance Properties#
SignalThe error value from a failed action. Cleared on reset.
this.errorState.asReadonly()Signaltrue for feedbackDuration ms after a failed action.
this.failedState.asReadonly()CngxAsyncStateFull CngxAsyncState view of this directive's lifecycle.
Bind to any state consumer ([state]="btn.state") to connect the
feedback system - toasts, alerts, skeletons, async containers.
buildAsyncStateView<unknown>({
status: this.status,
data: computed(() => undefined),
error: this.error,
lastUpdated: this.lastUpdatedState.asReadonly(),
})Signaltrue for feedbackDuration ms after a successful action.
this.succeededState.asReadonly()HostBindings#
| Binding | Expression |
|---|---|
[class.cngx-async--pending] | pending() |
[class.cngx-async--success] | succeeded() |
[class.cngx-async--error] | failed() |
[attr.aria-busy] | pending() || busy() || null |
[attr.aria-disabled] | pending() || !enabled() || null |
HostListeners#
| Event | Handler |
|---|---|
(click) | click() |