Skip to content

Revamp, Modern, Angular 21

Choose a tag to compare

@almothafar almothafar released this 06 Jun 15:18
· 64 commits to master since this release
3c6341f

[21.0.0] — 2026-06-04

⚠️ BREAKING CHANGES

  • Requires Angular 21. peerDependencies now require @angular/common and @angular/core
    ^21.0.0. Angular 20 and older projects must stay on @ngui/auto-complete@20 (or the matching major).
  • Removed NguiAutoCompleteModule. The library's last NgModule has been deleted. Import the
    standalone NguiAutoCompleteComponent / NguiAutoCompleteDirective directly instead:
    imports: [NguiAutoCompleteComponent, NguiAutoCompleteDirective]. Apps that still need the NgModule
    can stay on @ngui/auto-complete@20, which retained it.
  • The directive is now a ControlValueAccessor. NguiAutoCompleteDirective implements
    ControlValueAccessor and provides NG_VALUE_ACCESSOR, so [(ngModel)], [formControl] and
    formControlName integrate through Angular forms (with proper valueChanges, touched/dirty state).
    Consequences (see MIGRATION.md for before/after):
    • Removed the directive's bespoke ngModel input and its (ngModelChange) and
      (valueChanged) outputs. Use [(ngModel)] (Angular still emits (ngModelChange)) or a reactive
      form. (valueChanged) has no direct replacement — use (ngModelChange) or the control's valueChanges.
    • Removed the directive's custom [formControl] / formControlName inputs. The standard Angular
      directives now drive it directly — same template syntax, now with full form integration.
    • Wrapper-<div> usage: bind the value on the host
      (<div ngui-auto-complete [(ngModel)]="x"><input></div>) instead of on a separate inner <input>.
      Direct <input ngui-auto-complete [(ngModel)]="x"> is unchanged.
  • Unified the selection outputs into one (valueSelected) event. valueSelected + customSelected
    are merged into a single (valueSelected) (on both the component and directive) carrying
    NguiAutoCompleteSelection { value, item, index, fromSource }fromSource is true for a list pick,
    false for a typed value. (customSelected) is removed (check fromSource: false). The payload changed
    from the bare value to this object, so (valueSelected)="x = $event"(valueSelected)="x = $event.value"
    (or use [(ngModel)] / [(value)] for the value). The never-emitted textEntered output is also
    removed. (noMatchFound) is unchanged.
  • Consolidated value display into one display-with input. display-property-name and the
    string-template value-formatter are replaced by a single display-with that accepts either a
    property name or a function: display-with="name" or [display-with]="(item) => …". (list-formatter
    still formats the dropdown rows.) Migrate display-property-name="name"display-with="name", and
    value-formatter="(key) name"[display-with]="(item) => '(' + item.key + ') ' + item.name".
  • Removed the is-rtl input. Direction is now auto-detected from the input's computed direction, so
    an ancestor dir="rtl" (or the document direction) positions the dropdown correctly on its own — no
    input and no extra dependency. Replace [is-rtl]="true" with dir="rtl" on the element or an ancestor.
  • Replaced the innerHTML string templates with TemplateRefs. The string loading-template and
    header-item-template inputs are removed. Use the headerTemplate TemplateRef (already available) and
    the new loadingTemplate TemplateRef (loading-text remains for the simple case). This also removes
    an innerHTML sink.
  • source is now a required input. source uses input.required on both the component and directive
    (it was always required in practice — the control does nothing without it). Omitting [source] is now a
    compile-time error under strict templates (and a runtime error otherwise) instead of silently doing
    nothing. Just ensure every [ngui-auto-complete] / <ngui-auto-complete> has a [source].
  • New @angular/cdk peer dependency (directive dropdown now uses the CDK Overlay). The
    [ngui-auto-complete] directive renders its dropdown through @angular/cdk/overlay instead of inserting
    an absolutely-positioned element next to the input. You must install @angular/cdk@^21 and include the
    CDK overlay styles once in your app — either @import '@angular/cdk/overlay-prebuilt.css'; or any
    @angular/material theme (which already bundles them). See MIGRATION.md.

Added

  • [(value)] two-way binding on NguiAutoCompleteComponent. The standalone component now exposes a
    value model — e.g. <ngui-auto-complete [(value)]="myValue">. (valueSelected) continues to fire.
  • NguiAutoCompleteSelection<T> interface exported for the (valueSelected) payload
    ({ value, item, index, fromSource }).
  • Generic NguiAutoCompleteComponent<T = any>. Binding a typed [source] (array or function)
    infers the item type, so [(value)], (valueSelected) (NguiAutoCompleteSelection<T>) and the
    itemTemplate context are all typed without any annotation. Defaults to any, so existing templates
    are unaffected. The directive stays loosely typed (Angular can't infer generics for an attribute
    directive in templates).

Changed

  • Upgraded to Angular 21 (@angular/* 21.2.x, @angular/material + @angular/cdk 21.2.x).
    Install @ngui/auto-complete@21 for Angular 21 projects.
  • Signal-based inputs/outputs. All @Input()s are now signal input()s and all @Output()s are
    output()s on both the component and directive (the component's @ViewChilds became viewChild()
    queries). Template bindings are unchanged — every input keeps its existing name/alias (e.g.
    min-chars, list-formatter). The only consumer-visible effect is for code that reaches into a
    component/directive instance programmatically: those input properties are now read-only signals
    (call them, e.g. cmp.minChars()), not plain fields.
  • Typed attribute coercion. Numeric inputs (min-chars, max-num-list, z-index) now use
    numberAttribute and boolean inputs use booleanAttribute, so string-attribute forms (min-chars="2")
    and bound forms ([min-chars]="2") are both correctly typed.
  • OnPush dropdown component. NguiAutoCompleteComponent now uses ChangeDetectionStrategy.OnPush
    and its internal state (dropdownVisible, isLoading, filteredList, minCharsEntered, itemIndex)
    is signal-based, so remote/async results and directive-driven updates refresh reliably with less change
    detection work. No public API change.
  • Bumped the library's own tslib dependency floor to ^2.8.1.
  • The "drop-up" dropdown now anchors with the logical inset-inline-start instead of left, so it sits
    on the correct edge under RTL (matching the directive's positioning). No API change.
  • CDK Overlay positioning (directive). The dropdown now renders in a CDK overlay at the document root,
    so it escapes ancestor clipping / stacking contexts (e.g. inside a mat-form-field or a card with
    overflow: hidden) and flips above/below automatically on overflow. As a result, open-direction is now
    a preference (the overlay still flips when there isn't room), and z-index is rarely needed (the
    overlay already layers above page content; it now only orders overlapping overlays). RTL follows the
    input's computed direction. Note: because the dropdown is at the document root, custom dropdown
    styles (e.g. classes from a list-formatter) must be global, not scoped to an ancestor of the input.
  • Refreshed, themeable dropdown styling. The dropdown now has a subtle elevation, rounded corners, a
    softer border and roomier rows. Appearance is exposed through CSS variables — --ngui-ac-background,
    --ngui-ac-color, --ngui-ac-border, --ngui-ac-border-radius, --ngui-ac-shadow,
    --ngui-ac-max-height (none to remove the cap), --ngui-ac-item-padding, --ngui-ac-item-border,
    --ngui-ac-hover-background and --ngui-ac-selected-background — each with a sensible default (set them
    on :root). See the
    README "Theming" section. Applies to the directive and component.

Fixed

  • Internal keyword input a11y. The component's internal input (rendered when show-input-tag) now
    carries a unique id, clearing the browser's "a form field element should have an id or name" warning.
    It is bound standalone, so it never registers into a consumer's parent <form> (no stray
    keyword control) — which also fixes a latent error when the component was used inside a <form>.

Internal (development tooling only — no impact on consumers)

  • ng update to Angular 21: @angular/cli + @angular-devkit/build-angular 21.2.x,
    ng-packagr 21.2.5, TypeScript 5.9.3. The demo bootstrapApplication now provides
    provideZoneChangeDetection() (Angular 21 defaults to zoneless; this preserves the existing
    zone-based change detection).
  • ESLint 10. Bumped eslint and @eslint/js to 10 and angular-eslint to 21.4.0 (its ESLint peer
    now allows ^10), unblocking the upgrade deferred in 19.0.0 / 20.0.0.
  • The directive now forwards inputs to the dynamically created dropdown via ComponentRef.setInput()
    (required now that the component's inputs are read-only signals).

What's Changed

  • feat(angular-21)!: upgrade to Angular 21, remove NguiAutoCompleteModule, ESLint 10 by @almothafar in #490
  • refactor(lib): signal inputs/outputs + attribute transforms (Phase A, non-breaking) by @almothafar in #491
  • chore: add PR template and Copilot/Claude contributor guides by @almothafar in #492
  • refactor(lib): OnPush + signal state for the dropdown component (Phase B) by @almothafar in #493
  • feat(lib)!: ControlValueAccessor + [(value)] model (Phase C) by @almothafar in #494
  • feat(lib)!: Phase D — API ergonomics (unified event, display-with, RTL auto, TemplateRefs, generic, docs) by @almothafar in #495
  • refactor(demo): inline styles → SCSS + logical CSS; fix(lib): logical drop-up anchor by @almothafar in #496
  • fix(lib): unique id on internal keyword input (a11y, form-safe) by @almothafar in #497
  • feat(lib)!: make source a required input (proposal) by @almothafar in #498
  • feat(lib)!: render the directive dropdown via CDK Overlay by @almothafar in #499

Full Changelog: 20.0.0...21.0.0