Getting StartedInstallation and setup guides 5
LayoutWorkflow and structural layout components 7
OverlayModal and floating layer surfaces 3
FeedbackStatus, empty, progress, and loading placeholder patterns 5
FormInput and selection components 17
UtilityGeneral-purpose interface utilities 7
NavigationMenu surfaces and hierarchical actions 7

API reference

<tng-multi-autocomplete> wraps the headless primitive for the standard chip-based multi-select workflow. You pass options and accessors; bind query text when the parent owns local filtering or server-side search.

tng-multi-autocomplete

Wrapper attachment

html
<tng-multi-autocomplete
  [options]="filteredReleaseMarkets()"
  [value]="selectedMarkets()"
  (valueChange)="onSelectedMarketsChange($event)"
  [query]="marketQuery()"
  (queryChange)="marketQuery.set($event)"
  [getOptionValue]="getMarketValue"
  [getOptionLabel]="getMarketLabel"
  [isOptionDisabled]="isMarketDisabled"
  placeholder="Search launch markets"
  [ariaLabel]="'Launch markets'"
></tng-multi-autocomplete>
PropertyTypeDetails
optionsreadonly O[]Options rendered by the wrapper. Pass a pre-filtered list for local or server-side search.
value / valueChangereadonly V[] / outputControlled multi-select model containing the committed option values.
open / openChangeboolean / outputOptional controlled overlay state when the parent needs to observe or drive the menu.
query / queryChangestring / outputControlled trigger text for local filtering, debounced requests, or server-driven results.
disabled, loading, invalidbooleanForwarded primitive state inputs reflected onto the wrapper host for visuals and interaction guards.
placeholderstringTrigger input placeholder while no query text is present.
emptyTextstringFallback copy rendered when the filtered option list is empty.
ariaLabelstringAccessible name applied to the owned trigger input when no external label element is present.

Query and filtering

<tng-multi-autocomplete> exposes the trigger text through query and queryChange. The wrapper renders the options array exactly as provided, so local filtering and remote API requests both live in the parent component.

Local filtering

ts
readonly marketQuery = signal('');

readonly filteredReleaseMarkets = computed(() => {
  const query = this.marketQuery().toLowerCase().trim();

  if (!query) {
    return this.releaseMarkets;
  }

  return this.releaseMarkets.filter((market) =>
    market.label.toLowerCase().includes(query),
  );
});

Option accessors

The wrapper stays generic by delegating option identity, labels, disabled state, and tracking to small accessor functions.

Common accessors

ts
interface ReleaseMarketOption {
  readonly code: string;
  readonly label: string;
  readonly region: string;
  readonly disabled?: boolean;
}

readonly getMarketValue = (market: ReleaseMarketOption) => market.code;
readonly getMarketLabel = (market: ReleaseMarketOption) => market.label;
readonly isMarketDisabled = (market: ReleaseMarketOption) => market.disabled === true;
readonly trackMarket = (_index: number, market: ReleaseMarketOption) => market.code;
AccessorTypeDetails
getOptionValue(option: O) => VMaps each option object to the committed selection value stored in the model array.
getOptionLabel(option: O) => stringMaps each option object to the text used for chips and the default option template.
isOptionDisabled(option: O) => booleanDisables individual options while keeping them visible in the results list.
trackBy(index: number, option: O) => unknownCustom identity function for stable option rendering when the input list is refreshed.

Angular Signal Forms

<tng-multi-autocomplete> can bind directly with [formField] for readonly array fields. Bind query separately when the parent needs to filter options or request them from a server.

Signal forms wiring

ts
import { Component, signal } from '@angular/core';
import { FormField, form } from '@angular/forms/signals';
import { TngMultiAutocompleteComponent } from '@tailng-ui/components';

type MarketOption = {
  readonly code: string;
  readonly label: string;
};

@Component({
  selector: 'app-launch-markets-signal-form',
  standalone: true,
  imports: [FormField, TngMultiAutocompleteComponent],
  template: \`
    <tng-multi-autocomplete
      [formField]="launchForm.markets"
      [options]="markets"
      [getOptionValue]="getMarketValue"
      [getOptionLabel]="getMarketLabel"
      placeholder="Search launch markets"
      ariaLabel="Launch markets"
    ></tng-multi-autocomplete>
  \`,
})
export class LaunchMarketsSignalFormComponent {
  readonly launchModel = signal({
    markets: ['in'] as readonly string[],
  });
  readonly launchForm = form(this.launchModel);

  readonly markets: readonly MarketOption[] = [
    { code: 'in', label: 'India' },
    { code: 'sg', label: 'Singapore' },
    { code: 'us', label: 'United States' },
  ];

  readonly getMarketValue = (market: MarketOption) => market.code;
  readonly getMarketLabel = (market: MarketOption) => market.label;
}

Template hooks

You can replace the default chip body and option rows through content templates while the wrapper keeps the primitive selection logic intact.

Template hooks

html
<tng-multi-autocomplete
  [options]="releaseOwners"
  [value]="selectedOwners()"
  (valueChange)="onSelectedOwnersChange($event)"
  [getOptionValue]="getOwnerValue"
  [getOptionLabel]="getOwnerLabel"
>
  <ng-template #tngMultiAutocompleteChipTpl let-chip>
    <span>{{ chip.label }}</span>
    <button
      type="button"
      (click)="chip.removeItem(chip.value); $event.preventDefault(); $event.stopPropagation()"
      [attr.aria-label]="'Remove ' + chip.label"
    >
      ×
    </button>
  </ng-template>

  <ng-template #tngMultiAutocompleteOptionTpl let-option>
    <div>
      <strong>{{ option.label }}</strong>
      <small>{{ option.option.team }}</small>
    </div>
  </ng-template>
</tng-multi-autocomplete>
TemplateContextDetails
#tngMultiAutocompleteChipTplTemplateRef<{ option, value, label, removeItem }>Overrides the chip body while keeping the wrapper-owned chip container and remove semantics.
#tngMultiAutocompleteOptionTplTemplateRef<{ option, value, label, disabled, selected, active }>Overrides each option row for richer metadata layouts without rebuilding the primitive plumbing.

Primitive foundation

The wrapper is intentionally opinionated around the common “chips + input + results list” UX. Use the headless primitive when you need to rebuild the shell.

CapabilityAvailabilityGuidance
Query modelWrapper APIBind query/queryChange when a parent component needs local filtering, debounced requests, or server-driven results.
Overlay markupWrapper-ownedThe wrapper owns the trigger, chip host, overlay container, and listbox wiring for the common multi-tag experience.
Primitive escape hatchAvailableUse headless multi autocomplete when you need bespoke trigger markup or custom overlay structure.