@npcz/ngx-search-bar
v0.0.2
Published
Angular component for a customizable material search bar
Downloads
5
Maintainers
Readme
@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 |