Keep Angular Material Auto Complete open after selection
Asked Answered
S

2

7

I've used the angular material chip control and this all works fine. However it would be nice if the auto complete element stayed open so multiple selections could be made. In it's default state you have to move focus to a different control and back again to reopen the auto complete.

Is there an option I can set to keep the auto complete open until the user moves away completely from the control?

<mat-form-field class="example-chip-list">
  <mat-chip-list #chipList aria-label="Fruit selection">
    <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>


import {COMMA, ENTER} from '@angular/cdk/keycodes';
import {Component, ElementRef, ViewChild} from '@angular/core';
import {FormControl} from '@angular/forms';
import {MatAutocompleteSelectedEvent, MatAutocomplete} from '@angular/material/autocomplete';
import {MatChipInputEvent} from '@angular/material/chips';
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 = true;
  separatorKeysCodes: number[] = [ENTER, COMMA];
  fruitCtrl = new FormControl();
  filteredFruits: Observable<string[]>;
  fruits: string[] = ['Lemon'];
  allFruits: string[] = ['Apple', 'Lemon', 'Lime', 'Orange', 'Strawberry'];

  @ViewChild('fruitInput', {static: false}) fruitInput: ElementRef<HTMLInputElement>;
  @ViewChild('auto', {static: false}) matAutocomplete: MatAutocomplete;

  constructor() {
    this.filteredFruits = this.fruitCtrl.valueChanges.pipe(
        startWith(null),
        map((fruit: string | null) => fruit ? this._filter(fruit) : this.allFruits.slice()));
  }

  add(event: MatChipInputEvent): void {
    // Add fruit only when MatAutocomplete is not open
    // To make sure this does not conflict with OptionSelected Event
    if (!this.matAutocomplete.isOpen) {
      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);
  }
}
Sofa answered 5/7, 2019 at 9:54 Comment(2)
sounds like a feature requestGallon
See this post for an example of keeping the autocomplete open: https://mcmap.net/q/599090/-how-do-i-implement-autocomplete-in-a-lt-mat-select-gt-component. It might work for you.Marotta
B
0

If you place in your ts code something like

@ViewChild(MatAutocompleteTrigger, { read: MatAutocompleteTrigger }) matAutoComplete: MatAutocompleteTrigger;
...
selected(...){
...
   this.matAutoComplete.openPanel();
...

than the panel opens again. Perhaps you need to adjust the ChangeDetection (.detectChanges).

Additionally I would add in the html an $event.stopPropagation(), i.e.

<mat-autocomplete #auto="matAutocomplete" (optionSelected)="selected($event);$event.stopPropagation()">

to make sure no additional trigger is called.

Blithe answered 17/12, 2021 at 9:0 Comment(4)
ERROR TypeError: $event.stopPropagation is not a functionHaematothermal
What kind of type you get for $event?Blithe
MatAutocompleteSelectedEventHaematothermal
It didn't work with trigger.openPanel() for me. Instead what I did is to take ViewChild input and then blur native element and focus on it. I guess it works fine tooHaematothermal
T
0

AutoCompleteTrigger should do good. But there will be a blink when you select/unselect the checkbox

HMTL:

<input
        placeholder="New fruit..."
        #fruitInput
        #autocompleteTrigger="matAutocompleteTrigger"

<mat-option *ngFor="let fruit of filteredFruits | async" [value]="fruit" (click)="autocompleteTrigger.openPanel()">

Typescript:

import {MatAutocompleteTrigger} from '@angular/material/autocomplete';

@ViewChild('autocompleteTrigger') matACTrigger: MatAutocompleteTrigger;
Tent answered 16/7 at 12:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.