Velvet Star Monitor

Standout celebrity highlights with iconic style.

updates

Is there a way to make a multiselection in Autocomplete (Angular4+)?

Writer Emily Wong

I want to make a multi selection on filtered items by the auto complete. Inspired from the following tutorialI tried this code:

The component :

 <form> <mat-form-field> <input type="text" placeholder="Pick one" aria-label="Number" matInput [formControl]="myControl" [matAutocomplete]="auto"> <mat-autocomplete #auto="matAutocomplete"> <mat-option *ngFor="let option of filteredOptions | async" [value]="option" multiple> <mat-checkbox> {{ option }} </mat-checkbox> </mat-option> </mat-autocomplete> </mat-form-field>
</form>

I added tag to enable selection but it does not work. Once I filter and select one option the menu closes and the checkbox doesn't even get checked. Is there a way to make a multiselection in Autocomplete? Thank you !!

4 Answers

Angular Material documentation for chips includes a nice example of how to get started with a multiple selection autocomplete:

<mat-form-field> <mat-chip-list #chipList> <mat-chip *ngFor="let fruit of fruits" [selectable]="selectable" [removable]="removable" (removed)="remove(fruit)"> {{fruit}} <mat-icon matChipRemove *ngIf="removable">cancel</mat-icon> </mat-chip> <input placeholder="New fruit..." #fruitInput [formControl]="fruitCtrl" [matAutocomplete]="auto" [matChipInputFor]="chipList" [matChipInputSeparatorKeyCodes]="separatorKeysCodes" [matChipInputAddOnBlur]="addOnBlur" (matChipInputTokenEnd)="add($event)"> </mat-chip-list> <mat-autocomplete #auto="matAutocomplete" (optionSelected)="selected($event)"> <mat-option *ngFor="let fruit of filteredFruits | async" [value]="fruit"> {{fruit}} </mat-option> </mat-autocomplete>
</mat-form-field>

Basically, you have a chip list bound to a list and an text input that allows to search within an autocomplete list.

import {COMMA, ENTER} from '@angular/cdk/keycodes';
import {Component, ElementRef, ViewChild} from '@angular/core';
import {FormControl} from '@angular/forms';
import {MatAutocompleteSelectedEvent, MatChipInputEvent} from '@angular/material';
import {Observable} from 'rxjs';
import {map, startWith} from 'rxjs/operators';
/** * @title Chips Autocomplete */
@Component({ selector: 'chips-autocomplete-example', templateUrl: 'chips-autocomplete-example.html', styleUrls: ['chips-autocomplete-example.css'],
})
export class ChipsAutocompleteExample { visible = true; selectable = true; removable = true; addOnBlur = false; separatorKeysCodes: number[] = [ENTER, COMMA]; fruitCtrl = new FormControl(); filteredFruits: Observable<string[]>; fruits: string[] = ['Lemon']; allFruits: string[] = ['Apple', 'Lemon', 'Lime', 'Orange', 'Strawberry']; @ViewChild('fruitInput') fruitInput: ElementRef; constructor() { this.filteredFruits = this.fruitCtrl.valueChanges.pipe( startWith(null), map((fruit: string | null) => fruit ? this._filter(fruit) : this.allFruits.slice())); } add(event: MatChipInputEvent): void { const input = event.input; const value = event.value; // Add our fruit if ((value || '').trim()) { this.fruits.push(value.trim()); } // Reset the input value if (input) { input.value = ''; } this.fruitCtrl.setValue(null); } remove(fruit: string): void { const index = this.fruits.indexOf(fruit); if (index >= 0) { this.fruits.splice(index, 1); } } selected(event: MatAutocompleteSelectedEvent): void { this.fruits.push(event.option.viewValue); this.fruitInput.nativeElement.value = ''; this.fruitCtrl.setValue(null); } private _filter(value: string): string[] { const filterValue = value.toLowerCase(); return this.allFruits.filter(fruit => fruit.toLowerCase().indexOf(filterValue) === 0); }
}

The code is pretty straightforward (filter based on text input after each typed character, remove an item from the list etc.). Of course, this should be tweaked to your needs (e.g. once you select an item, it should be filtered out for next autocomplete results).

3

This is a little late to the party but I found an excellent stackBlitz here . Reproducing the code here, written by the owner:

HTML:

<mat-form-field> <input type="text" placeholder="Select Users" aria-label="Select Users" matInput [matAutocomplete]="auto" [formControl]="userControl"> <mat-hint>Enter text to find users by name</mat-hint>
</mat-form-field>
<mat-autocomplete #auto="matAutocomplete" [displayWith]="displayFn"> <mat-option *ngFor="let user of filteredUsers | async" [value]="selectedUsers"> <div (click)="optionClicked($event, user)"> <mat-checkbox [checked]="user.selected" (change)="toggleSelection(user)" (click)="$event.stopPropagation()"> {{ user.firstname }} {{ user.lastname }} </mat-checkbox> </div> </mat-option>
</mat-autocomplete>
<br><br>
<label>Selected Users:</label>
<mat-list dense> <mat-list-item *ngIf="selectedUsers?.length === 0">(None)</mat-list-item> <mat-list-item *ngFor="let user of selectedUsers"> {{ user.firstname }} {{ user.lastname }} </mat-list-item>
</mat-list>

Typescript:

import { Component, OnInit, ViewChild } from '@angular/core';
import { FormControl } from '@angular/forms';
import { Observable } from 'rxjs';
import { map, startWith } from 'rxjs/operators';
export class User { constructor(public firstname: string, public lastname: string, public selected?: boolean) { if (selected === undefined) selected = false; }
}
/** * @title Multi-select autocomplete */
@Component({ selector: 'multiselect-autocomplete-example', templateUrl: 'multiselect-autocomplete-example.html', styleUrls: ['multiselect-autocomplete-example.css']
})
export class MultiselectAutocompleteExample implements OnInit { userControl = new FormControl(); users = [ new User('Misha', 'Arnold'), new User('Felix', 'Godines'), new User('Odessa', 'Thorton'), new User('Julianne', 'Gills'), new User('Virgil', 'Hommel'), new User('Justa', 'Betts'), new User('Keely', 'Millington'), new User('Blanca', 'Winzer'), new User('Alejandrina', 'Pallas'), new User('Rosy', 'Tippins'), new User('Winona', 'Kerrick'), new User('Reynaldo', 'Orchard'), new User('Shawn', 'Counce'), new User('Shemeka', 'Wittner'), new User('Sheila', 'Sak'), new User('Zola', 'Rodas'), new User('Dena', 'Heilman'), new User('Concepcion', 'Pickrell'), new User('Marylynn', 'Berthiaume'), new User('Howard', 'Lipton'), new User('Maxine', 'Amon'), new User('Iliana', 'Steck'), new User('Laverna', 'Cessna'), new User('Brittany', 'Rosch'), new User('Esteban', 'Ohlinger'), new User('Myron', 'Cotner'), new User('Geri', 'Donner'), new User('Minna', 'Ryckman'), new User('Yi', 'Grieco'), new User('Lloyd', 'Sneed'), new User('Marquis', 'Willmon'), new User('Lupita', 'Mattern'), new User('Fernande', 'Shirk'), new User('Eloise', 'Mccaffrey'), new User('Abram', 'Hatter'), new User('Karisa', 'Milera'), new User('Bailey', 'Eno'), new User('Juliane', 'Sinclair'), new User('Giselle', 'Labuda'), new User('Chelsie', 'Hy'), new User('Catina', 'Wohlers'), new User('Edris', 'Liberto'), new User('Harry', 'Dossett'), new User('Yasmin', 'Bohl'), new User('Cheyenne', 'Ostlund'), new User('Joannie', 'Greenley'), new User('Sherril', 'Colin'), new User('Mariann', 'Frasca'), new User('Sena', 'Henningsen'), new User('Cami', 'Ringo') ]; selectedUsers: User[] = new Array<User>(); filteredUsers: Observable<User[]>; lastFilter: string = ''; ngOnInit() { this.filteredUsers = this.userControl.valueChanges.pipe( startWith<string | User[]>(''), map(value => typeof value === 'string' ? value : this.lastFilter), map(filter => this.filter(filter)) ); } filter(filter: string): User[] { this.lastFilter = filter; if (filter) { return this.users.filter(option => { return option.firstname.toLowerCase().indexOf(filter.toLowerCase()) >= 0 || option.lastname.toLowerCase().indexOf(filter.toLowerCase()) >= 0; }) } else { return this.users.slice(); } } displayFn(value: User[] | string): string | undefined { let displayValue: string; if (Array.isArray(value)) { value.forEach((user, index) => { if (index === 0) { displayValue = user.firstname + ' ' + user.lastname; } else { displayValue += ', ' + user.firstname + ' ' + user.lastname; } }); } else { displayValue = value; } return displayValue; } optionClicked(event: Event, user: User) { event.stopPropagation(); this.toggleSelection(user); } toggleSelection(user: User) { user.selected = !user.selected; if (user.selected) { this.selectedUsers.push(user); } else { const i = this.selectedUsers.findIndex(value => value.firstname === user.firstname && value.lastname === user.lastname); this.selectedUsers.splice(i, 1); } this.userControl.setValue(this.selectedUsers); }
}
2

I would suggest using Ng2-select:

 <ng-select [virtualScroll]="true" #select [items]="items" [multiple]="true" bindLabel="name" placeholder="items..." formControlName="myFormCOntrolName"> </ng-select>

It is styled using angular material. The virtualScroll option isn't well documented at present, but is very fast for large data sets.

Ng2-select, try the multiple one, , let me know if this is what you want.

In general, there's no good standard for multiple selection.

1

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.