Angular Material autocomplete component is a text input that shows suggestions while you type. A common validation requirement for an autocomplete control is to force the user to pick an option.

Unfortunately, the autocomplete component does not validate what you type in the input box against the available options.

You have you either click on the option or navigate with arrow keys and press the enter key to make a selection. In most cases though, you want the user to make a selection to validate date entry.

Autocomplete Force Selection

I recently posted a solution in this two year old thread in GitHub that discusses this issue. Several people liked the solution so I want to share it here in my blog.

You can get the full source code from GitHub. Alternatively, run the live demo below.

Write a custom validator

Since I was using reactive forms in my project, the easiest way to solve this was to use a custom validator. I have done the following:

Firstly, create a new file requireMatch.ts and add the following function:

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 will evaluate the user input and return a validation error to the form control. Special thanks to Shinigami92 for optimisation.

Import the function into your component and pass it into the form control constructor.

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[]; constructor() { this.projects = [ 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)

Listen to the validation errors on the view like so:

<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 this post, 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

Umut is a certified Microsoft certified developer and has an MSc in Computer Science. He is currently working as a senior software developer in Edinburgh, UK. He is the primary author and the founder of onthecode.

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;
    }

Leave a Reply