如何在<mat-select>组件中实现自动完成?

时间:2018-05-21 17:07:46

标签: angular angular-material angular-material2

我想将自动完成功能与多选择mat-select选择器结合使用,因为选项列表会很长。

我已经在stackoverflow上搜索了答案,最接近答案的是implement a search filter for the <mat-select> component of angular material

然而,这些例子是关于表而不是mat-select。

我的问题是,是否可以将自动完成功能添加到mat-select。如果没有,我可以在列表中的每个项目前面创建包含复选框的自动填充功能吗?

谢谢

编辑:我发现angular的primefaces有一个多选列表,允许你搜索listitems。它还包括一个内置的全选按钮!你可以在https://www.primefaces.org/primeng/#/multiselect

找到它

您可以使用npm install primeng --save

安装主要面

3 个答案:

答案 0 :(得分:11)

您可以使用MatAutocomplete和一些技巧实现自动完成多项选择。我相当确定你不能用MatSelect来做,因为那并不能让你控制输入元素。这项技术有点奇怪,但它运作正常。我们的想法是管理控制器中的选择,并设置&#34;值&#34;每个mat-option对同一件事 - 您选择的选项。您还需要捕获点击以允许用户与列表进行交互(而不是在单击时立即关闭),当然还要在mat-option项内提供复选框。其他一些技巧也是必要的 - here is a quick and dirty example显示了该怎么做。

<强> HTML:

<mat-form-field class="example-full-width">
    <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>

<强> TS:

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

}

答案 1 :(得分:3)

一种方法是在mat-select内添加一个手动过滤器,这样它将具有自动完成功能。 查找以下给定解决方案here

的示例代码

添加用于过滤文字的输入控制器

    public filterControl = new FormControl();

在mat-select内添加过滤器文本字段

     <mat-select
        [id]="fieldId"
        [formControl]="custonDropdown"
        (selectionChange)="onSelectChange($event.value)"
        (closed)="onPanelClose()"
        multiple
        panelClass="custom-mat-select">
        <input matInput 
             [formControl]="filterControl" 
             type="text" 
             name="filter-options" 
             id="filter-options" 
             placeholder="Search">
        <mat-option *ngFor="let option of filteredOptions | async"
             [value]="option.value" 
             [ngClass]="{'hide': !option.show}">
            {{option.name | translate}}
        </mat-option>
    </mat-select>

现在添加事件侦听器以更改过滤器文本字段的值

     this.filteredOptions = this.filterControl.valueChanges.pipe(
            startWith(''),
            map((value: string) => {
                this.optionItems
                    .forEach(option => {
                        option.show = option.name.toLocaleLowerCase().includes(value.toLowerCase());
                    });
                return this.optionItems;
            })
        );

也要使整个列表在下一个下拉菜单中显示,请清除模板关闭时的过滤器文本字段,如模板代码(closed)="onPanelClose()"

所述
 onPanelClose() {
    this.filterControl.setValue('');
}

Demo

答案 2 :(得分:0)

是的,您可以使用PrimeNG多选控件。唯一的问题是如果您担心主题。

还有一个使用 mat-select-autocomplet

的选项

如何安装:

npm install select-autocomplete --save

npm:https://www.npmjs.com/package/mat-select-autocomplete

演示:https://stackblitz.com/edit/mat-select-autocomplete