In this post, we’ll look at how to validate autocomplete component from Angular Material. Working with reactive forms, we’ll write a custom validator to force selection only from the list of options.

The autocomplete component is an input with a dropdown of suggested options. It allows the user to type into the input and filter list of options. Then, the user can choose an option.

Unfortunately, it does not validate text value against the list of options.

Autocomplete Force Selection

Create custom validator for autocomplete

Working with reactive forms, the easiest way to solve this issue is to write a custom form validator.

Firstly, let’s create a new file requireMatch.ts:

import { AbstractControl } from '@angular/forms'; export function RequireMatch(control: AbstractControl) { const selection: any = control.value; if (typeof selection === 'string') { return { incorrect: true }; } return null; }
Code language: TypeScript (typescript)

This function evaluates the value of a control and return a validation error if the value is a string type.

In detail, AbstractControl parameter allows us to read the current value of any form control. If the type of value is just text, we return an error code incorrect. Otherwise, we return null to indicate no errors.

All form controls can make use of this validator function.

Use validator in form

We can now import the function into your component and pass it into the form control constructor.

In the snippet below, I created a simple FormGroup with an autocomplete control to select a project from a list. In addition to adding a required validator, we specify RequireMatch validator on the validators array.

import { Component, OnInit } from '@angular/core'; import { FormGroup, FormControl, Validators } from '@angular/forms'; import { Project } from './project'; import { RequireMatch as RequireMatch } from './requireMatch'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit { form: FormGroup; projects: Project[] = [ new Project("Web Development"), new Project("UX"), new Project("SEO") ]; ngOnInit() { this.form = new FormGroup({ project: new FormControl('', [Validators.required, RequireMatch]), }); } displayWith(obj?: any): string | undefined { return obj ? obj.name : undefined; } }
Code language: TypeScript (typescript)

With the form group is configured, we are now able to display an error message in the template:

<form [formGroup]="form"> <mat-form-field class="full-width"> <input type="text" placeholder="Project" matInput formControlName="project" [matAutocomplete]="projectAutoComplete"> <mat-autocomplete #projectAutoComplete="matAutocomplete" [displayWith]="displayWith"> <mat-option *ngFor="let project of projects" [value]="project"> {{ project.name }} </mat-option> </mat-autocomplete> <mat-error *ngIf="form.controls['project'].hasError('required')"> Please enter a value </mat-error> <mat-error *ngIf="form.controls['project'].hasError('incorrect')"> Please select a valid project </mat-error> </mat-form-field> </form>
Code language: HTML, XML (xml)

Source code is available on GitHub.

Summary

In summary, I explained how to implement a custom validator to force option selection for Angular Material autocomplete component. This validator basically makes the autocomplete a hybrid between a select and an autocomplete.

Umut Esen

I am a software developer and blogger with a passion for the world wide web.

Leave a Reply

This Post Has 16 Comments

  1. Ankur

    It doesn’t work with [value]=”project.name”

    1. Umut Esen

      Hi Ankur, thank you for your comment. What error message are you getting?

  2. Eric Gehring

    When I enter a valid projectname – not by selection – it is rejected as well. I think the user will not understand this behaviour. Anyway, the input and the autocomplete – being aware of each other – should tackle this in the first place, f.e. after loosing focus. But maybe it can and I missed some property.

    1. Umut Esen

      You’re correct, typing in a valid value does not automatically select the option – the user must click on an option or use keyboard arrow keys to highlight & press the enter key. Unfortunately, this appears to be the behaviour of the autocomplete component.

      Feel free to create a PR for this or raise this as an issue at https://github.com/angular/material

  3. Test

    Your validator can only be used once on one autocomplete and will not work on second autocomplete?

    1. Umut Esen

      You can register the validator with as many autocomplete form controls as you wish!

  4. James

    Thanks for this solution, i found it quite clean and simple

  5. Ruben Heymans

    Thanks for this easy solution!
    It would also be nice to match on an object of type x instead of just object.
    Also do you know how how to match the data with and id of an object and not entire object? I save the id’s in my database.

  6. Randy

    How can I apply an Angular Material Autocomplete Force Selection solution using template-driven forms?

  7. harsh

    while submit form i am not getting required error

    1. Umut Esen

      Hi, do you see any errors in the console (hit F12 to see)? Also, make sure the control key matches your form control defined in the component.

  8. Netaji

    This works well. Thanks!

    I’ve a problem though, with the given example the Autocomplete is loaded well but is not searchable. To make it searchable I’ve tried using [formControl] on top of existing code as below and the list just doesn’t load at all, what am I missing?

    Template:

    {{skillItem.name}}

    Please enter a value

    Please select a valid skill

    Component:

    this.form = new FormGroup({
    skill: new FormControl(”, [Validators.required, this.RequireMatch]),
    });

    this.filteredSkillData = this.skillMatAutocompleteControl.valueChanges
    .pipe(
    startWith(null),
    map(name => name ? this._filter(name) : this.skillData.slice())
    );

    private _filter(value: any): Skill[] {
    value = (value && typeof value === ‘string’) ? value : value.name;
    const filterValue = value.toLowerCase();
    return this.skillData.filter(skill => skill.name.toLowerCase().includes(filterValue));
    }

  9. Netaji

    Basically I am checking for a filter feature enabled with this approach, could you please do the needful, Thanks.

  10. abhilash wankhade

    well my auto complete is dynamic and I am getting data from database so following part is giving error.
    Could you tell me how to deal with it.

    displayWith(obj?: any): string | undefined {
    return obj ? obj.name : undefined;
    }