Advanced

Patterns for power users — dynamic options, controlled state, Reactive Forms, SSR safety, and accessibility.

Inject field options at runtime

Supply a field's option list from your app at runtime — live data like contacts, products, or locations, or options computed from what the user typed. Overrides are keyed per field and fully replace the server's options for that field.

  1. First, make sure the field exists as a runtime-injected custom field: in Edit Logic, answer Yes to the custom data fields question, add the field's name (e.g. Product Color), and pick "I will inject the values at runtime" as its type. Save so your product regenerates with the new field.
  2. Pass optionOverrides keyed by the field's name exactly as you typed it in Edit Logic — no renaming or case conversion. A field saved as pizza_type is keyed pizza_type; one saved as "pizza type" is keyed "pizza type". Whenever that field's pill becomes active, your function is called with the text the user has typed against the pill and returns the options to show.
  3. The returned list replaces the server's options for that field entirely — return every option you want visible, and an empty array when nothing matches.
option-overrides.component.ts
// "Product Color" added as a runtime-injected custom field in Edit Logic.
// The key must match the field name exactly as typed there.
overrides = {
"Product Color": (query: string) =>
this.colors // your live data
.filter((c) => c.toLowerCase().includes(query.toLowerCase()))
.map((c) => ({ text: c, is_tappable: true, kind: null })),
amount: (query: string) => {
const digits = query.replace(/\D/g, "");
if (!digits) return [{ text: "$100", is_tappable: true, kind: null }];
return [{ text: `$${digits}`, is_tappable: true, kind: null }];
},
};
// <ai-autocomplete [optionOverrides]="overrides" (submitted)="onSubmit($event)" />

Controlled mode

Drive state from outside the component with [value]/(valueChange) and [completedParams]/(completedParamsChange) — handy inside wizards, multi-step forms, or a shared parent.

controlled.component.ts
@Component({
template: `
<ai-autocomplete
[value]="text"
(valueChange)="text = $event"
[completedParams]="params"
(completedParamsChange)="params = $event"
(submitted)="onSubmit($event)"
></ai-autocomplete>
`,
})
export class ControlledComponent {
text = "";
params: CompletedParamState[] = [];
}

Or bind a Reactive Forms control: both <ai-autocomplete> and the [aiaInput] directive implement ControlValueAccessor, so [formControl] and [(ngModel)] work out of the box.

SSR / Angular Universal

The component is safe to pre-render with Angular Universal. The live autocomplete boots on the client (the controller is created in ngOnInit and the contentEditable wiring runs in ngAfterViewInit, both guarded by isPlatformBrowser), while the server still emits the wrapper with the correct data-mode so styling doesn't shift on hydration. For Tier 2, gate new AIAutocompleteController() behind isPlatformBrowser — it requires a browser environment.

Accessibility

The component implements the ARIA combobox pattern. Things you should know:

  • The dropdown uses role="listbox" with aria-activedescendant pointing at the highlighted option.
  • Pills are buttons (role="button") and announce as part of the live input.
  • Keyboard: arrow keys navigate, Enter submits, Tab autocompletes, Escape closes the dropdown.