Autocomplete

The idea with autocomplete is to help the user narrow down what possible values an input field can have. In a normal input field, you would just type something and hope a validation would tell you if what you input is incorrect. With autocomplete, you are presented with a list as you type. The list is narrowed down as you type, and at any point you can decide to stop typing and instead select an item from the list. This is a time saver as you don't have to type the entire item's name, and it also enhances correctness as the user is being made to select from a list, rather than to type in the whole thing.

With this being the complete behavior of autocorrect, it means that we need to provide it with a list of possible answers and also an input box in which to receive the input.

We need to set up this control in five steps:

  1. Import and register all the necessary modules with our root module.
  2. Define a mat-form-field, containing an input control.
  3. Define a mat-autocomplete control, this is the list of possible options.
  4. Link the two controls through a view reference.
  5. Add a filter that filters down the autocomplete control when the user types.

Let's start with the first step, all our necessary imports. Here we need the autocomplete functionality, but as we will be working with forms, and in particular reactive forms, we are going to need that module as well. We will also need some forms to support the input fields we mean to use:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { MatButtonModule } from '@angular/material';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { MatIconModule } from '@angular/material/icon';
import { MatButtonToggleModule } from '@angular/material/button-toggle';
import { MatAutocompleteModule } from '@angular/material';
import { ReactiveFormsModule } from '@angular/forms';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input'
;

@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
BrowserAnimationsModule,
MatButtonModule,
MatIconModule,
MatButtonToggleModule,
MatAutocompleteModule,
ReactiveFormsModule,
MatFormFieldModule,
MatInputModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }

Now we are ready to add some markup to the app.component.html file template:

<mat-form-field>
<input type="text" matInput placeholder="jedis" [formControl]="myControl" >
</mat-form-field>

At this point, we have defined the input control and added the matInput directive. We have also added a formControl reference. We add that so we can later on listen to changes to our input as they happen. Changes to an input are interesting because we are able to react to them and filter our list, which is essentially what autocomplete does. The next order of business is to define a list of values that we need to suggest to the user once they start typing, so let's do that next:

<mat-autocomplete #auto="matAutocomplete">
<mat-option *ngFor="let jedi of jedis" [value]="jedi">
{{ jedi }}
</mat-option>
</mat-autocomplete>

We have our list, but we lack any connection between the input field and our suggestion list. Before we fix that, we first need to look at our component class and add some code to it to support the previous markup:

export class AppComponent {
myControl: FormControl;
jedis = [
'Luke',
'Yoda',
'Darth Vader',
'Palpatine',
'Dooku',
'Darth Maul'
];

constructor() {
this.myControl = new FormControl();
}
}

So far we have defined matInput and mat-autocomplete separately, now it's time to connect the two. We do that by adding a view reference to mat-autocomplete that matInput can refer to, like so:

<mat-autocomplete #auto="matAutocomplete">
<mat-option *ngFor="let jedi of jedis"
[value]="jedi">
{{ jedi }}
</mat-option>
</mat-autocomplete>

And to refer to it in matInput, we introduce the MatAutocomplete directive, like so:

<form action="">
<mat-input-container name="container">
<mat-form-field hintLabel="Max 30 characters">
<input name="input"
type="text"
#input
m
atInput
placeholder="type the name of the jedi"
[formControl]="jediControl"
[matAutocomplete]= "auto">
<mat-hint align="end">{{input.value?.length || 0}}/30</mat-hint>
</mat-form-field>
</mat-input-container>
</form>

As you can see, matAutocomplete points to the auto view reference, thereby the list is triggered when we set focus to the input field and start typing.

We have added another useful thing in the preceding code, namely that of hints. Adding hints to your input is a great way to convey to the user what should be entered in the input field. Adding the attribute hintLabel, we are able to tell the user what should go into the input field. You can even take it a step further by introducing a tip on how they are doing while they are typing by using the mat-hint element. Let's zoom in on the preceding code that accomplished what we just described:

<mat-form-field hintLabel="Max 30 characters">
<input name="input"
type="text"
#input
m
atInput
placeholder="type the name of the jedi"
[formControl]="jediControl"
[matAutocomplete]= "auto">
<mat-hint align="end">{{input.value?.length || 0}}/30</mat-hint>
</mat-form-field>

Try to make use of the hintLabel and mat-hint element where applicable, it will help your users greatly.

If you typed everything in correctly, you should have something that looks like this in the UI:

Looks great! The list is displayed when you put the input in focus. However, you notice that the list is not really being filtered down as you type. This is because we haven't picked up on the event when you type into the input control. So let us do that next.

Listening to input changing means we listen to our form control and its valueChanges property, like so:

myControl.valueChanges

If you look closely, you can see that this is an Observable. This means that we can use operators to filter out content we don't want. Our definition for wanted content is jedis, that starts with the text we entered in the input box. This means we can flesh it out to look something like this:

import { Component } from '@angular/core';
import { FormControl } from "@angular/forms";
import { Observable } from "rxjs/Observable";
import 'rxjs/add/operator/map';

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'app';
myControl: FormControl;
jedis = [
'Luke',
'Yoda',
'Darth Vader',
'Palpatine',
'Dooku',
'Darth Maul'
];

filteredJedis$: Observable<string[]>;

constructor() {
this.myControl = new FormControl();
this.filteredJedis$ = this.myControl
.valueChanges
.map(input => this.filter(input
));
}

filter(key: string): Array<string> {
return this.jedis.filter(jedi => jedi.startsWith(key));
}

}

Now we just need to change our template so the mat-option looks at filteredJedis instead of the jedis array, like so:

<mat-autocomplete #auto="matAutocomplete">
<mat-option *ngFor="let jedi of filteredJedis$ | async"
[value]="jedi">
{{ jedi }}
</mat-option>
</mat-autocomplete>

Testing this out, we see that it seems to work.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset