Skip to main content
cngx-src documentation

CngxAsyncClick

DirectivePrimaryv0.1.0WCAG AA

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>

https://cngxjs.github.io/cngx/examples/#/common/interactive/retry/optimistic-instant-like-toggle https://cngxjs.github.io/cngx/examples/#/common/interactive/retry/createretry-cngxasyncclick

Metadata#

Host#

Relationships

Index#

Inputs#

input()Required

The async action to execute on click.

default { alias: 'cngxAsyncClick' }
autoAnnounce#boolean
input()

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.

default true
input()

External 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]).

default false
enabled#boolean
input()

When 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.

default true
failedAnnouncement#string
input()

Label announced to screen readers on failure.

default 'Action failed'
feedbackDuration#number
input()

Duration in ms to show success/error state before reset.

default 2000
succeededAnnouncement#string
input()

Label announced to screen readers on success.

default 'Action succeeded'

Instance Properties#

error#Signal
Readonly

The error value from a failed action. Cleared on reset.

this.errorState.asReadonly()
failed#Signal
Readonly

true for feedbackDuration ms after a failed action.

this.failedState.asReadonly()
pending#Signal
Readonly

true while the action is executing.

this.pendingState.asReadonly()
Readonly

Full 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(),
})
succeeded#Signal
Readonly

true for feedbackDuration ms after a successful action.

this.succeededState.asReadonly()

HostBindings#

BindingExpression
[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#

EventHandler
(click)click()