Demo Angular bindings over the same vanilla core Full check-in demo →

Same core, Angular bindings

The library is a plain async function with no framework dependency. A binding owns three things: the responder list the component renders (resolved from a policy), the one call, and its state. This page's injectable service is all of it; the React example is the same as a hook.

The whole binding

@Injectable({ providedIn: "root" })
export class CheckinService {
  readonly responders = signal<Responder[]>([]);
  readonly status = signal<Status>("idle");
  readonly response = signal<SmartCheckinResponse | undefined>(undefined);

  // who may answer in this browser — availability and the default come back in the list
  async configure(policy) {
    this.policy = policy;
    this.responders.set(await resolveResponders(policy));
  }

  async request(input, responder = this.responders().find((r) => r.isDefault)) {
    this.status.set("waiting");
    try {
      this.response.set(await requestCheckin(input, {
        getCredential: credentialGetterFor(responder, { origin: this.policy.origin }),
      }));
      this.status.set("done");
    } catch (e) {
      this.status.set(e instanceof CheckinFlowError && e.outcome.status === "declined"
        ? "declined" : "error");
    }
  }
}

// …and the component renders the list: one button per responder, the default leading
@for (r of checkin.responders(); track r.id) {
  <button [class]="r.isDefault ? 'smart-btn primary' : 'smart-btn'"
          [disabled]="!r.available" [title]="r.reason" (click)="start(r)">
    {{ r.name }}
  </button>
}

Compiled in the browser (JIT) so this page needs no Angular build step — which is why the bundle is large. A real app compiles ahead of time and ships only its own code plus this library.