Angular-Material-Autocomplete icon indicating copy to clipboard operation
Angular-Material-Autocomplete copied to clipboard

Angular Material Autocomplete component

Angular Material Autocompelte

This is a wrapper for standard mat-autocomplete, made to simplify and minimize work with autocomplete inputs. It extends traditional behavior with new functionality such as form control binding, fetch from API service or data array, prefetch items on init, loading animation, template render for options in list.

See the demo here.

example

Getting started

Install package via npm

npm install @vguleaev/angular-material-autocomplete

Requires Angular Material to be installed.

npm install @angular/material @angular/cdk

Import module with

import { NgMatAutocompleteModule } from '../../node_modules/@vguleaev/angular-material-autocomplete';

Use component somewhere in html template

<ng-mat-autocomplete> </ng-mat-autocomplete>

For more examples click here.

Documentation

Required attributes are marked as bold text.

source and displayItem or displayItemFn are required attributes.

Attribute Default Value Description
source null Source can be a simple any[ ] or service that implements 'AutocompleteService' interface.

If source is a service, on every key press component will call a service.fetch(params) method and return a Promise.
Argument params is object of type HttpParams that will containt one single param called "query" having current input value. Any additional params can be passed with serviceParams attribute.

If source is an array, a local filter is done. Result of displayItem or displayItemFn functions (which is a string) is checked if it contains input value.

For more examples click here.
displayItem 'item.name' String. This string will be evaluated with eval() function to show formatted value in autocomplete suggestions list. Often result data is array of objects. If you want to display some property use 'item.myprop' string. The 'item' word is required.

When search is done via service, search logic is done on the server. When source is an array, search logic is simple comparing strings. Result of the displayItem/displayItemFn and input value will be compared. Eval function is dangerous and error prone. Better use displayItemFn which is a function.
displayItemFn null Function. If not null, displayItemFn will be used instead of displayItem string to show item in autocomplete list. Example of display function:
(country: string) => country.name + country.code
displayTemplate null Should be a value of type TemplateRef. When this value is not null an html template will be rendered for each option in autocomplete list.

Please note that search is done by comparing input value with displayItem or displayItemFn executing result.

For more examples click here.
serviceParams null Should be a value of type HttpParams. This params together with 'query' param will be passed to a service fetch function.
floatLabel "auto" Type of FloatLabelType. Set label floating behavior. Can be 'always' or 'never' or 'auto'.
placeholder "Search" Text for placeholder.
minChars 2 Number of minimum chars requered to start search. Default is 2. Set to 0 if you want all the values available on empty input.
doPrefetch false Should be boolean. When active, a prefetch is done on component initalization. Requires a service in source. After prefetch is done, result is stored in memory and search is done on local array.
clearAfterSearch false Boolean. When active, input clears after item select.
hasProgressBar false Boolean. When active, shows a loading bar under input. Animation displays while request is doing. Works only when source is setup as service.
hasSearchButton false Boolean. Shows a search button near input. When button pressed s force search is done. Even if minChars value is bigger than current input text length, this button will start search.
(optionSelected) event Output event. Clicking on any suggested option will raise this event. Event returns current selected option as a param.
showAddNew false Boolean. When active, shows a create button when no suggestions are found. Click here for example.
addNewText "Add new" String. Text to display near create button.
(createNew) event Output event. Clicking on create new button raises this event and pass current input value as event param.
transformResult x: any[] => x Function. Is used to format data returned from the server. Used only when source is a service.
validationErrors [ ] String[ ]. Every sting in array displays as mat-error under input.
isFocused false Boolean. Sets the focus on autocomplete on component init.

Please note that on focus search is triggered.

Examples

Component can be used as any form control because of implementing ControlValueAccessor.

Basic usage

Miminal number of attributes is one. You can create an items array of objects and pass it to source attribute.

  // in ts file
  items = [
    { code: '0', name: 'Red' },
    { code: '1', name: 'Blue' },
    { code: '2', name: 'Green' },
    { code: '3', name: 'Yellow' },
    { code: '4', name: 'Black' },
    { code: '5', name: 'Purple' },
    { code: '6', name: 'White' },
    { code: '7', name: 'Grey' },
  ];
  
  // in html template
  <ng-mat-autocomplete [source]="items"> </ng-mat-autocomplete>

This will just work. Because of property of objects called name. Default displayItem string is 'item.name'. If you want to show and search by property code, you can specify it in displayItem property.

  <ng-mat-autocomplete [source]="items" [displayItem]="'item.code'"> </ng-mat-autocomplete>

You can also use disaplyItemFn attribute and provide an anonymous function. It helps on syntax checking, strong typing and refactor tools available.

  // in ts file
  displayItem = (x: any) => '# ' + x.code + ' name: ' + x.name.toUpperCase();
  
  // in html template
  <ng-mat-autocomplete [source]="items" [displayItemFn]="displayItem"> </ng-mat-autocomplete>

If your items are simple array of strings. Use diaplayItem = "item". Word "item" is requried for code evaluation.

Using service as a source

You need to pass a service with implements AutocompleteService interface into source proprerty.

@Injectable()
export class DataService implements AutocompleteService {
    private _url = 'api/colors'; // fake api url

    constructor(private _http: HttpClient) {}

    fetch(params?: HttpParams): Promise<any> {
        return this._http.get<any[]>(this._url, {params: params}).toPromise();
    }
}

Get this service into constructor via dependency injection and pass it to autocompelte source.

  // in ts file
  constructor(public dataService: DataService) {}
  
  // in html template
  <ng-mat-autocomplete [source]="dataService" [displayItemFn]="displayItem"> </ng-mat-autocomplete>

We are expecting return data from server as an array of objects. If for example we get another format returned from the server like

{
  items: any[],
  total: nubmer
}

You can use transformResult attribute to format the data returned from the server.

  // in ts file
  formatData = (data: any) => data.items;
  
  // in html template
  <ng-mat-autocomplete [source]="dataService" 
                       [displayItemFn]="displayItem"
                       [transformResult]="formatData"> 
  </ng-mat-autocomplete>

To get a selected option subscribe to optionSelected event.

  <ng-mat-autocomplete [source]="dataService" 
                       [displayItemFn]="displayItem"
                       [transformResult]="formatData"> 
                       (optionSelected)="select($event)"  
  </ng-mat-autocomplete>
  // in ts file
  public select(color: any) {
    alert('You selected color:' + color.name);
  }

Template usage

You can use an html template to display options in list. diplayItem and displayItemFn result will be replaced with template render, but local search (if source is array) will be anyway done by comparing display functions result.

To do this simply define local template variable and assign it to displayTempalte attribute.

  // in html template
  <ng-mat-autocomplete [source]="dataService" 
                       [displayItemFn]="displayItem"
                       [displayTemplate]="itemTemplate"> 
  </ng-mat-autocomplete>
  
   <ng-template #itemTemplate let-item>
      <mat-icon>flag</mat-icon> {{item.name}} | {{item.code}}
  </ng-template>

Add new suggestion

You can implement a possibility to create a new option if no suggestion found. To do this set attribute showAddNew to true.

alt text

You can change the default text to any other string using addNewText attribute. Clicking on that button will raise an event (createNew) with current input text as a param. Subscribe to this event to implement the logic.

  // in html template
  <ng-mat-autocomplete [source]="items"
                       [showAddNew] = "true
                       [addNewText] = "'Add new country'"
                       (createnew)="createNew($event)"> 
  </ng-mat-autocomplete>
  // in ts file
  public createNew(value: string) {
    const newName = prompt('Enter new name for a country', value);
    this.items.push({code: '99', name: newName});
    this.items = this.items.slice(0);
  }

All available attributes example

How to use this component:

<ng-mat-autocomplete
    [placeholder]="'Search country'"
    [floatLabel] = "'always'"
    [source] = "dataService"    
    [serviceParams]= "params"               
    [minChars] = "2"                            
    [doPrefetch]= "false"                       
    [clearAfterSearch] = "false"                
    [hasProgressBar] = "false"                 
    [hasSearchButton] = "false"                 
    [validationErrors]="errors"                 
    [transformResult] = "formatCountries"               
    [isFocused]="true" 
    (optionSelected)="selectCountry($event)"     
     
    [displayItem] = "'item.name'"               
    [displayItemFn] = "displayCountry"                
    [displayTemplate] = "countryTemplate"           
     
    [showAddNew] = "false"                      
    [addNewText] = "'Add new'"                 
    (createNew) = "onCreateNew($event)"   
                         
    [formControl]="form.controls['country']"   
    [(ngModel)]="model.country"                      
    (ngModelChange)="countryChanged(country)"
>
</ng-mat-autocomplete>