Searchbar

The <smart-searchbar> component provides a debounced search input with an optional toggle button. It follows the Base + Standard + Wrapper pattern with an InjectionToken-based extension mechanism. It renders a default SearchbarStandardComponent which can be replaced via SEARCHBAR_STANDARD_COMPONENT_TOKEN.


Usage

<smart-searchbar [options]="options" [(text)]="query" [(show)]="expanded" />
@if (query()) {
  <p>Results for "{{ query() }}"</p>
}

Components

Main wrapper component. Renders SearchbarStandardComponent by default. When SEARCHBAR_STANDARD_COMPONENT_TOKEN is provided, renders the injected component via NgComponentOutlet.

SearchbarStandardComponent (<smart-searchbar-standard>)

Default concrete implementation. Renders:

  • a Tailwind-styled <input type="search"> bound to a UntypedFormControl with debounceTime from options,
  • a magnifier SVG icon inside the input,
  • an optional toggle button (shown when show() is false and options.showToggleButton is true) that reveals the input,
  • smart:dark:* dark-mode classes.

SearchbarBaseComponent (abstract)

Abstract base directive for extending custom searchbar implementations. Exposes options, cssClass, show and text models, a control signal wrapping UntypedFormControl, and setShow()/tryHide() methods. The base class wires control.valueChanges through debounceTime() in ngAfterViewInit and emits into the text model.

API

Inputs

InputTypeDefaultDescription
optionsInputSignal<ISearchbarOptions | undefined>-Searchbar configuration
showModelSignal<boolean>trueWhether the input is visible (two-way bindable)
textModelSignal<string>requiredDebounced search text (two-way bindable)
classInputSignal<string>''External CSS classes (alias for cssClass)

ISearchbarOptions

The placeholder value is rendered through TranslatePipe, so it may be a translation key. If no placeholder is provided, the key 'search' is used.

SEARCHBAR_STANDARD_COMPONENT_TOKEN

InjectionToken that allows replacing the default SearchbarStandardComponent with a custom implementation. Provide a Type<SearchbarBaseComponent> to override.

Extending the base class

import {
  ChangeDetectionStrategy,
  Component,
  computed,
  input,
  signal,
  ViewEncapsulation,
} from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';

import {
  ISearchbarOptions,
  SEARCHBAR_STANDARD_COMPONENT_TOKEN,
  SearchbarBaseComponent,
  SearchbarComponent,
} from '@smartsoft001/angular';

@Component({
  selector: 'docs-custom-searchbar',
  template: `
    @if (show()) {
      <div [class]="containerClasses()">
        <span class="docs-searchbar__icon" aria-hidden="true">&#9906;</span>
        <input
          type="search"
          class="docs-searchbar__input"
          [formControl]="control()"
          [placeholder]="options()?.placeholder ?? 'Search'"
          [attr.aria-label]="options()?.label ?? 'Search'"
          (blur)="tryHide()"
        />
      </div>
    } @else if (options()?.showToggleButton) {
      <button
        type="button"
        class="docs-searchbar__toggle"
        aria-label="Show the search field"
        (click)="setShow()"
      >
        &#9906;
      </button>
    }
  `,
  imports: [ReactiveFormsModule],
  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CustomSearchbarComponent extends SearchbarBaseComponent {
  // NgComponentOutlet passes 'cssClass' by canonical name, not the 'class' alias.
  override cssClass = input<string>('');

  // The base class owns the debounced control.valueChanges subscription that
  // feeds `text` - do not subscribe again here.
  readonly containerClasses = computed(() => {
    const classes = ['docs-searchbar'];
    const extra = this.cssClass();
    if (extra) classes.push(extra);
    return classes.join(' ');
  });
}

@Component({
  selector: 'docs-searchbar-custom-example',
  changeDetection: ChangeDetectionStrategy.OnPush,
  imports: [SearchbarComponent],
  // The token swaps the standard searchbar for the custom one everywhere below
  // this component, so consumers keep writing `<smart-searchbar>`.
  providers: [
    {
      provide: SEARCHBAR_STANDARD_COMPONENT_TOKEN,
      useValue: CustomSearchbarComponent,
    },
  ],
  // NgComponentOutlet forwards `show` and `text` as plain inputs, so values
  // travel down into the custom component but its own model changes never
  // travel back up to these signals.
  template: `
    <smart-searchbar [(show)]="show" [(text)]="text" [options]="options" />
  `,
})
export class SearchbarCustomExampleComponent {
  readonly show = signal(true);
  readonly text = signal('');

  readonly options: ISearchbarOptions = {
    placeholder: 'Search invoices',
    label: 'Search invoices',
    debounceTime: 300,
    showToggleButton: true,
  };
}

Source

The component lives in packages/shared/angular/src/lib/components/searchbar and is documented for Claude Code by the angular-components-searchbar skill.