我正在 Angular 中开发一个应用程序,我主要使用 ng-select 来搜索可搜索的下拉菜单,它的工作方式就像一个魅力。现在我正在将整个应用程序迁移到角度材料,并且我面临着一个问题,试图使角度材料垫自动完成工作就像 ng-select 一样。
之前,在编辑现有数据时使用 ng-select 时,我只需要做一个 patchValue 并将选项值设置到控制器中,即可使其相应的标签出现在相应的 ng-select 中。这是 select 的默认行为。当迁移到 mat-autocomplete 时,它不会像这样工作。我知道我可以使用 mat-select 代替,但搜索功能对我来说是关键。
我已经浏览了角度材料文档,并通过使用
displayWith
属性,我能够进行 mat-autocomplete 工作,但我必须将整个选项对象修补到表单控件中,这不是我想要的后端并不是为了以这种方式处理它而开发的。
为了让您了解我在说什么,下面是该场景的示例代码:
HTML
<form [formGroup]="fruitFormBootstrap">
<div class="row">
<div class="col-4">
<label for="fruits" class="form-label">Fruits</label>
<select id="fruits" class="form-select" formControlName="fruit">
@for (fruit of fruits; track $index) {
<option [value]="fruit.value">{{ fruit.label }}</option>
}
</select>
</div>
</div>
<div>
<button class="btn btn-primary" (click)="submitBootstrapForm()">Submit</button>
</div>
</form>
<hr>
<form [formGroup]="fruitFormMaterial">
<mat-form-field class="example-full-width">
<mat-label>Fruits</mat-label>
<input type="text"
placeholder="Pick one"
aria-label="Fruits"
matInput
formControlName="fruit"
[matAutocomplete]="auto">
<mat-autocomplete #auto="matAutocomplete" [displayWith]="displayFn">
@for (fruit of fruits; track fruit.value) {
<mat-option [value]="fruit">{{fruit.label}}</mat-option>
}
</mat-autocomplete>
</mat-form-field>
<button mat-raised-button (click)="submitAngularMaterialForm()">Submit</button>
</form>
打字稿:
import {Component, OnInit} from '@angular/core';
import {MatInputModule} from "@angular/material/input";
import {MatAutocompleteModule} from "@angular/material/autocomplete";
import {FormBuilder, FormControl, FormGroup, ReactiveFormsModule} from "@angular/forms";
import {MatFormFieldModule} from "@angular/material/form-field";
import {MatButtonModule} from "@angular/material/button";
import {MatListModule} from "@angular/material/list";
import {MatSelectModule} from "@angular/material/select";
@Component({
selector: 'app-root',
standalone: true,
imports: [
MatFormFieldModule,
MatInputModule,
MatAutocompleteModule,
MatSelectModule,
ReactiveFormsModule,
MatButtonModule,
MatListModule
],
templateUrl: './app.component.html',
styleUrl: './app.component.css'
})
export class AppComponent implements OnInit {
fruits: Array<{ value: number, label: string }>;
fruitFormBootstrap: FormGroup;
fruitFormMaterial: FormGroup;
constructor(private fb: FormBuilder) {
this.fruits = [
{ value: 1, label: 'Watermelon' },
{ value: 2, label: 'Apple' },
{ value: 3, label: 'Banana' }
];
this.fruitFormBootstrap = this.fb.group({
fruit: new FormControl()
});
this.fruitFormMaterial = this.fb.group({
fruit: new FormControl()
});
}
// Patching a value in bootstrap automatically selects its corresponding label for display
ngOnInit() {
this.fruitFormBootstrap.patchValue({
fruit: 2
});
//angular material autocompelte patching only updates the controller without updating the display to show its corresponding label
// this.fruitFormMaterial.patchValue({
// fruit: 2
// });
const searchFruit = this.fruits.find(fruit => fruit.value === 2);
//although this works but it sets the entire object as form control value which is not what I want
this.fruitFormMaterial.patchValue({
fruit: searchFruit
})
}
//submitting a select in bootstrap set the form control with the value of the selected option
submitBootstrapForm() {
console.log(this.fruitFormBootstrap);
}
//Based on angular material documentation this is how to have a separate display value than the control
displayFn(option: { value: number, label: string }) {
return option ? option.label : '';
}
submitAngularMaterialForm() {
console.log(this.fruitFormMaterial);
}
}
在上面的代码中,您可以看到,对于引导选择,我只需修补选项的值,选择会自动选择其相应的标签并显示它。但 mat-autocompelete 的情况并非如此。我必须设置整个对象才能正常工作。如果我选择一个选项并单击提交按钮,也会发生同样的事情。 bootstrap select 将控件设置为值,但 mat-autocomplete 将其设置为整个选定的水果对象(我猜这是预期的,因为这是提供给
mat-option
[value]
属性绑定的内容)。
我已经尝试了一些
mat-option
[value]
属性绑定和 displayFn
实现,但没有任何效果。当我使用 fruit.value
进行 [value]
属性绑定时,表单控件会使用所选值而不是整个所选对象进行正确更新,但不会显示标签。
注意:请注意,我可以以相同的形式拥有多个 mat-autocomplete,所有这些都引用不同的数据(但选项结构对于所有数据都是相同的)
您可以通过基于
displayFn
数组中的 value
进行搜索来修改 fruits
以显示所选选项的标签。请注意,您需要将其更改为箭头函数才能访问组件变量。
displayFn = (value: string) => {
if (Number.isNaN(value)) return value;
return (
this.fruits?.find((fruit) => fruit.value === Number(value))?.label ||
value
);
};
在您看来,将
fruit.value
应用于 [value]
属性。
<mat-autocomplete #auto="matAutocomplete" [displayWith]="displayFn">
@for (fruit of fruits; track fruit.value) {
<mat-option [value]="fruit.value">{{ fruit.label }}</mat-option>
}
</mat-autocomplete>