概述
在表单的设计过程中,会有一些表单字段需要在已知的内容中进行选择,这在html中会使用select组件来设计该表单字段。而在Material中,同样有与之对应的 <mat-select>
组件。这种组件在选项内容较少的情况下使用非常方便,
能很好的引导用户在表单中,输入规范的内容:
但是在选项内容过多时,这种 <mat-select>
组件,对用户来说,想要找到自己想要的选项就比较麻烦,需要一个个选项去对比,查找:
幸运的是,在Material中提供了 mat-autocomplete
,这个组件和我们以前在JQuery时代使用的select2类似。它支持用户键盘输入功能,并根据输入内容在已有的数据集合中进行过滤,选项内容动态的显示过滤后的结果。
MatAutocomplete
基本用法
html:
<form class="example-form">
<mat-form-field class="example-full-width">
<input type="text" placeholder="Pick one" aria-label="Number" matInput [formControl]="myControl" [matAutocomplete]="auto">
<mat-autocomplete #auto="matAutocomplete">
<mat-option *ngFor="let option of options" [value]="option">
{{option}} </mat-option>
</mat-autocomplete>
</mat-form-field></form>
TypeScript:
import {Component} from '@angular/core';import {FormControl} from '@angular/forms';/**
* @title Simple autocomplete
*/@Component({
selector: 'autocomplete-simple-example',
templateUrl: 'autocomplete-simple-example.html',
styleUrls: ['autocomplete-simple-example.css'],
})export class AutocompleteSimpleExample {
myControl = new FormControl();
options: string[] = ['One', 'Two', 'Three'];
}
呈现的效果如下:
高级API
在简单用法中,我们发现,选项内容为字符串,选择中的值和显示的值为相同的。但在实际的应用过程中,我们需要选中的值和显示的内容是不同的。
displayWith
举个例子,我们有个需要选择企业的字段,要存储到数据库的值是企业的编码,而在界面上显示的需要是企业的名称。
在这个例子中,我们有个企业的数据结构:
interface Company {
code: string;
name: string;
}
companies: Company[] = []; constructor() { this.companies.push({code: 'qiye-01', name: '企业01'}); this.companies.push({code: 'qiye-02', name: '企业02'}); this.companies.push({code: 'qiye-03', name: '企业03'}); this.companies.push({code: 'qiye-04', name: '企业04'});
}
<form class="example-form">
<mat-form-field class="example-full-width">
<input type="text" placeholder="Pick one" aria-label="Number" matInput [formControl]="myControl" [matAutocomplete]="auto">
<mat-autocomplete #auto="matAutocomplete">
<mat-option *ngFor="let option of companies" [value]="option">
{{option.name}} </mat-option>
</mat-autocomplete>
</mat-form-field></form>
在html中,我们将mat-option的显示内容修改成name,如 {{option.name}}
。这样我们的下拉选项中就会显示企业的名称:
但是如果我们选中其中一个选项,就会发现结果和我们想象的不一样,选中后的结果显示并不是我们想要的企业名称
此时,我们就需要使用API中提供的
在MatAutocomplete的API: displayWith。文档已经很明确的告诉我们 displayWith
需要我们传入一个已定义的函数
@Input()
displayWith: ((value: any) => string) | null
其中的函数形参value指的是 <mat-option *ngFor="let option of companies" [value]="option">
中value对应的option,此处option就是一个我们后台定义的Company。这样我们的value就拥有code和name两个属性。
这样我们就可以定义一个displayWith的函数:
displayFn(value: any): string { // value: Company
return value ? value.name : undefined;
}
同时修改html
<mat-autocomplete #auto="matAutocomplete" [displayWith]="displayFn">
<mat-option *ngFor="let option of companies" [value]="option">
{{option.name}} </mat-option></mat-autocomplete>
这样就会显示我们想要的结果
在这种方式下,我们的formControl得到的其实是一个company对象,要存储code,需要在从company对象中获取code属性值。
那么如果我们想在formControl中直接得到code呢?
扩展用法
通过分析,我们发现,formControl获得值的内容,其实和中的value是一致的。
因此,我们可以做如下修改, 将value改成option.code
<form class="example-form">
<mat-form-field class="example-full-width">
<input type="text" placeholder="Pick one" aria-label="Number" matInput [formControl]="myControl" [matAutocomplete]="auto">
<mat-autocomplete #auto="matAutocomplete">
<mat-option *ngFor="let option of companies" [value]="option.code">
{{option.name}} </mat-option>
</mat-autocomplete>
</mat-form-field></form>
得到如下显示结果:
发现显示的是code值,那我们还可以使用displayWith函数,让它显示企业名称吗?
答案是可以的。不过我们就不能在使用上面定义的displayFn方法了。
此时我们需要重新定义一个更高级的方法:
displayWith() { return (value) => { if (value) { const arr = that.companies.filter(item => item.code === value); if (arr.length) { return arr[0].name;
}
} return undefined;
}
}
在html中的使用方法如下:
<form class="example-form">
<mat-form-field class="example-full-width">
<input type="text" placeholder="Pick one" aria-label="Number" matInput [formControl]="myControl" [matAutocomplete]="auto">
<mat-autocomplete #auto="matAutocomplete" [displayWith]="displayWith()">
<mat-option *ngFor="let option of companies" [value]="option.code">
{{option.name}} </mat-option>
</mat-autocomplete>
</mat-form-field></form>
需要[displayWith]=”displayWithThis(this)” 这样特殊的使用方法。
为什么同样是用displayWith,不同的value中,实际写法差异却如此大呢?
这个和JS的This作用于有关。
在不是用匿名函数的情况下,如:
displayFn(value) { if (value) { const arr = this.companies.filter(item => item.code === value); if (arr.length) { return arr[0].name;
}
} return undefined;
}
此时,如果将displayFn直接设置到mat-autocomplete中,此时的this表示mat-autocomplete对象,这样this.companies.filter就会抛出异常。
结论
在本文中,我们主要讲了MatAutocomplete在实际开发过程中,在不同的业务场景中,同样的组件我们用到的不同的实现方式。每一种语言都有它自己的特性,掌握了这些特性,会让我们工作时更加得心应手。