@npcz/ngx-search-bar
TypeScript icon, indicating that this package has built-in type declarations

0.0.2 • Public • Published

@npcz/ngx-search-bar

An angular material custom component that implements a search bar. The bar has the usual input field, a search button and can optionally have the following additional features:

  • automatic highlighting on hover, focus or both, with the highlight style being configurable (background, shadow or outline)
  • a clear button that shows only when the search input is not empty and can be clicked to clear the input field
  • a select component that can be used to chose a search category to restrict the data source used for search results
  • ability to connect the search field to an angular material autocomplete component provided by the host of the search bar

Installation

npm install @npcz/ngx-search-bar --save

or

yarn add @npcz/ngx-search-bar

Basic Usage

1. Import the SearchBarModule

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { SearchBarModule } from '@npcz/ngx-search-bar';

import { AppComponent } from './app.component';

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule, SearchBarModule],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}

2. Add the configuration properties to the component

Specify the highlighting style you want to be applied by defining a variable in the component file of type HighlightStyleConfig

type HighlighStyle = 'shadow' | 'outline' | 'background' | 'none';
type HighlightStyleConfig = {
  hover?: HighlighStyle;
  focus?: HighlighStyle;
};
import { Component } from '@angular/core';
import { HighlightStyleConfig } from '@npcz/ngx-highlight';

@Component({
  selector: 'exmp-app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
})
export class AppComponent {
  readonly highlightStyle: HighlightStyleConfig = {
    hover: 'background',
    focus: 'outline',
  };

  // categories can be provided through a Service or any other suitable source
  // in a real application
  readonly categories = [
    'All products',
    'Health',
    'Computers & Accessories',
    'Electronics',
    'Home & Kitchen',
  ];
}

3. Include a hc-search-bar component in the template

<hc-search-bar
  [highlightStyle]="highlightStyle"
  [categoryList]="categories"
  category="All Products"
  clearButton="true"
>
</hc-search-bar>

4. Include the theming support styles in your app

ngx-search-bar uses SASS styling and provides mixins that can be integrated into and angular material application theme. Simply follow the instructions for Angular Material Theming. The theme file is located in node_modules/@npcz/ngx-search-bar/src/styles.

Adding search suggestions with autocomplete

The component has zero dependencies on services or providers from the application to implement search related functions. Instead it expects the host component to configure it following parent->child communication model with Input properties. All child->parent communication uses Observables.

Here is an example of a host component providing the autocomplete implementation to the search bar and using its observables:

<hc-search-bar
  [highlightStyle]="highlightStyle"
  [categoryList]="categories"
  category="All Products"
  clearButton="true"
>
</hc-search-bar>
<!-- 
  The autocomplete template is provided by the host to completely decouple
  the search bar from the search implementation.
-->
<mat-autocomplete #auto="matAutocomplete" autoActiveFirstOption="true">
  <mat-option *ngIf="isLoading" class="is-loading"
    ><mat-spinner diameter="40"></mat-spinner
  ></mat-option>
  <ng-container *ngIf="!isLoading">
    <mat-option
      *ngFor="let suggestion of searchSuggestions | async"
      [value]="suggestion"
    >
      <span>{{ suggestion }}</span>
    </mat-option>
  </ng-container>
</mat-autocomplete>
export class GlobalSearchBarComponent implements AfterViewInit, OnDestroy {
  @ViewChild(MatAutocomplete) private _autocomplete: MatAutocomplete;
  @ViewChild(SearchBarComponent) private _searchBar: SearchBarComponent;

  // Automatic unsubscription when the component is destroyed
  // We don't want an emitted value, just the fact of emitting is enough
  private _ngUnsubscribe$ = new Subject<never>();

  private _searchSuggestions = new BehaviorSubject<string[]>([]);
  searchSuggestions: Observable<
    string[]
  > = this._searchSuggestions.asObservable();

  categories = [
    'All products',
    'Health',
    'Computers & Accessories',
    'Electronics',
    'Home & Kitchen',
  ];

  isLoading = false;

  constructor(private _http: HttpClient, private _cd: ChangeDetectorRef) {
  }

  // Hook into the search bar event observables after the view is initialized
  // and the @ViewChild bindings become valid
  ngAfterViewInit(): void {
    this._searchBar.autoComplete = this._autocomplete;
    this._cd.detectChanges();

    // Get involved everytime a change to the selected category or to the
    // search input field occurs.
    // => load search suggestions here
    this._searchBar.changes
      .pipe(
        debounceTime(300),
        map((value) => {
          if (!value || !value.query || value.query.length == 0) {
            // Reset the search suggestions
            this._searchSuggestions.next([]);
          } else {
            this._loadSearchSuggestion(value);
          }
        }),
        takeUntil(this._ngUnsubscribe$)
      )
      .subscribe();

    // Know when the search needs to be done (user clicked the search button or
    // hit the 'Enter' key)
    this._searchBar.search
      .pipe(takeUntil(this._ngUnsubscribe$))
      .subscribe((value) => {
        console.debug('submit: ', value);
        // Reset the search suggestions
        this._searchSuggestions.next([]);
      });
  }

  ngOnDestroy(): void {
    this._ngUnsubscribe$.next();
    this._ngUnsubscribe$.complete();
  }

  // Implementation of the search suggestions.
  // Example using goolge search suggestions rest API
  private _loadSearchSuggestion(searchOptions): void {
    const params = new HttpParams();
    params.set('output', 'firefox');
    params.set('q', searchOptions.query);

    console.debug('loading search suggestions: ', searchOptions);
    this.isLoading = true;
    this._http
      .get(
        '/complete/search?output=firefox&q=' +
          encodeURI(searchOptions.query) +
          '&hl=en',
        {
          params: params,
        }
      )
      .pipe(
        map((res) => res[1]),
        take(1),
        catchError(() => {
          this._searchSuggestions.next([]);
          this.isLoading = false;
          return throwError('Failed to get search suggestions.');
        })
      )
      .subscribe((suggestions) => {
        this._searchSuggestions.next(suggestions);
        this.isLoading = false;
      });
  }
  private handleError(error: HttpErrorResponse) {
    if (error.error instanceof ErrorEvent) {
      // A client-side or network error occurred. Handle it accordingly.
      console.error('An error occurred:', error.error.message);
    } else {
      // The backend returned an unsuccessful response code.
      // The response body may contain clues as to what went wrong,
      console.error(
        `Backend returned code ${error.status}, ` + `body was: ${error.error}`
      );
    }
    // return an observable with a user-facing error message
    return throwError('Something bad happened; please try again later.');
  }

Properties

Property Type Default Description
placeholder string none Placeholder text for the search input field
clearButton boolean false Adds a 'clear input' button when true
disabled boolean false Makes the search bar 'disabled'
highlightStyle HighlightStyleConfig { hover: 'background', focus: 'outline' } Specifies whether and how the search bar style will change when it is hovered or focused
categoryList string[] undefined When set to a valid value, this will add a category selection control and a menu with the values in the list to the search bar
category string undefined The currently selected category from the categoryList
autoComplete MatAutoComplete undefined The angular material autocomplete component to be used by the search bar for search suggestions. If undefined, no search suggestions functionality will be provided. See example for how to add search suggestions to the search bar

Events

Event Type Description
changes SearchQueryData Triggered any time the category or the search input field value changes. Event details will contain the category (optional) and the search input field values
search SearchQueryData Triggered when a search must be initiated. Event details will contain the category (optional) and the search query

Methods

Method Description
clear() Clear the search input field
enable() Enable/activate the search bar
disable() Disable/deactivate the search bar
focus() Set focus the search bar, which will automatically set focus to the search input field

Package Sidebar

Install

npm i @npcz/ngx-search-bar

Weekly Downloads

0

Version

0.0.2

License

BSD-3-Clause

Unpacked Size

117 kB

Total Files

39

Last publish

Collaborators

  • abdesassi