Angular Material: mat-select无法选择默认值

167

我有一个 mat-select,其中所有选项都是在数组中定义的对象。我试图将该值设置为默认选项之一,但在页面呈现时,它仍然被选中。

我的 TypeScript 文件包含:

  public options2 = [
    {"id": 1, "name": "a"},
    {"id": 2, "name": "b"}
  ]
  public selected2 = this.options2[1].id;

我的HTML文件包含:

  <div>
    <mat-select
        [(value)]="selected2">
      <mat-option
          *ngFor="let option of options2"
          value="{{ option.id }}">
        {{ option.name }}
      </mat-option>
    </mat-select>
  </div>

我已经尝试将mat-option中的selected2value设置为对象及其ID,并在mat-select中同时使用了[(value)][(ngModel)],但都没有起作用。

我正在使用Material版本2.0.0-beta.10。


2
使用 compareWith,更加优雅。 - Badis Merabet
必须要有 compareWith,请参考这里的badis答案 https://dev59.com/JFYN5IYBdhLWcg3whIXz - bresleveloper
19个回答

197

在您的模板中使用绑定来表示值。

value="{{ option.id }}"

应该是

[value]="option.id"

在您选择的值中,请使用ngModel而不是value

<mat-select [(value)]="selected2">

应该是

<mat-select [(ngModel)]="selected2">

完整代码:

<div>
  <mat-select [(ngModel)]="selected2">
    <mat-option *ngFor="let option of options2" [value]="option.id">{{ option.name }}</mat-option>
  </mat-select>
</div>

顺便提一下,从2.0.0-beta.12版本开始,material select现在可以接受一个mat-form-field元素作为父元素,以使其与其他材料输入控件保持一致。在升级后,请将div元素替换为mat-form-field元素。

<mat-form-field>
  <mat-select [(ngModel)]="selected2">
    <mat-option *ngFor="let option of options2" [value]="option.id">{{ option.name }}</mat-option>
  </mat-select>
</mat-form-field>

14
看起来你正在使用ngModel与formControlName在同一表单字段上。 在Angular v6中,已经停止支持在响应式表单指令中同时使用ngModel输入属性和ngModelChange事件,并且将在Angular v7中删除此功能。有关更多信息,请参阅此处的API文档: https://angular.io/api/forms/FormControlName#use-with-ngmodel - ldgorman
1
@ldgorman - 我不明白你是如何得出那个结论的。如果你指的是mat-form-field,那么这是“用于包装多个Angular Material组件并应用常见的文本字段样式”,因此不是同一件事。除此之外,OP和我的答案都没有提到FormControlFormGroupFormControlName - Igor
1
即使我实现了与@Igor相同的代码,我仍然遇到了相同的问题。 - Chuck
3
我们找到了问题所在,返回的值是一个数字,而Mat-select需要的是一个字符串。我们使用了 [compareWith] 指令来解决这个问题。 - Chuck
1
在Angular v6中,已经弃用了使用ngModel输入属性和ngModelChange事件与响应式表单指令一起使用的支持,并将在Angular v7中删除。这个答案不再适用。请将其定义为最高支持到v6或将其删除。 - axelrotter
显示剩余5条评论

154

请使用compareWith函数,用于将选项值与所选值进行比较。详情请参见此处:https://material.angular.io/components/select/api#MatSelect

对于以下结构的对象:

listOfObjs = [{ name: 'john', id: '1'}, { name: 'jimmy', id: '2'},...]

像这样定义标记:

<mat-form-field>
  <mat-select
    [compareWith]="compareObjects"
    [(ngModel)]="obj">
       <mat-option  *ngFor="let obj of listOfObjs" [value]="obj">
          {{ obj.name }}
       </mat-option>
    </mat-select>
</mat-form-field>

然后像这样定义比较函数:

compareObjects(o1: any, o2: any): boolean {
  return o1.name === o2.name && o1.id === o2.id;
}

18
处理对象时非常完美,而不是简单的数组。谢谢。 - Riaan van Zyl
ngModel现在已经被弃用了,所以我们必须使用formControlName,但在Angular 16中仍然可以使用使用比较函数的解决方案。 - undefined

27

我正在使用Angular 5和响应式表单与mat-select,但无法使上述任何一种解决方案显示初始值。

我必须添加[compareWith]以处理mat-select组件内使用的不同类型。在内部,似乎mat-select使用数组来保存所选值。如果打开了多选模式,则可以通过此方式允许相同的代码工作。

Angular Select Control Doc

这是我的解决方案:

使用Form Builder初始化表单控件:

this.formGroup = this.fb.group({
    country: new FormControl([ this.myRecord.country.id ] ),
    ...
});

然后在您的组件上实现compareWith函数:

compareIds(id1: any, id2: any): boolean {
    const a1 = determineId(id1);
    const a2 = determineId(id2);
    return a1 === a2;
}

接下来创建并导出determineId函数(我必须创建一个独立的函数,以便mat-select可以使用它):

接下来创建并导出determineId函数(我必须创建一个独立的函数,以便mat-select可以使用它):

export function determineId(id: any): string {
    if (id.constructor.name === 'array' && id.length > 0) {
       return '' + id[0];
    }
    return '' + id;
}

最后将compareWith属性添加到您的mat-select中:

<mat-form-field hintLabel="select one">
<mat-select placeholder="Country" formControlName="country" 
    [compareWith]="compareIds">

    <mat-option>None</mat-option>
    <mat-option *ngFor="let country of countries" [value]="country.id">
                        {{ country.name }}
    </mat-option>
</mat-select>
</mat-form-field>

22

以下是将其绑定为 [value]mat-option 中的示例:

<mat-select placeholder="Panel color" [(value)]="selected2">
  <mat-option *ngFor="let option of options2" [value]="option.id">
    {{ option.name }}
  </mat-option>
</mat-select>

实时演示


这个方法完美无误,相比使用ngModel或setValue()更加简单。 - Rohit Parte

13

你可以简单地实现自己的比较函数

[compareWith]="compareItems"

另请参见文档. 因此完整代码将如下所示:

  <div>
    <mat-select
        [(value)]="selected2" [compareWith]="compareItems">
      <mat-option
          *ngFor="let option of options2"
          value="{{ option.id }}">
        {{ option.name }}
      </mat-option>
    </mat-select>
  </div>

同时在 Typescript 文件中:

  compareItems(i1, i2) {
    return i1 && i2 && i1.id===i2.id;
  }

这对我有用,我认为这是大多数正确的方法,但如果列表只包含一个元素,则不起作用。谢谢。 - Code Kadiya
如果只有一个元素,你会得到什么样的异常?因为比较应该考虑到 i1i2 是否存在。 - LeO

9

当页面加载时,我在绑定第一个选项上遇到了问题。以下是帮助我的解决方案。

.html

<mat-form-field appearance="outline">    
    <mat-select #teamDropdown 
        [ngModel]="selectedGroupId" (selectionChange)="selectedGroupId=$event.value">
        <mat-option value=undefined>Please Select</mat-option>
        <mat-option *ngFor="let grp of groups" [value]="grp.groupsId">
            {{grp.groupName}}
        </mat-option>
    </mat-select>
</mat-form-field>

.ts

@ViewChild('teamDropdown') teamDropdown: MatSelect;
ngAfterViewInit() {
    setTimeout(() => {
        this.teamDropdown.options.first.select();
    });
}

你的意思是:this.teamDropdown.options.first.select(); - kingabdr

9

如已在Angular 6中提到,在响应式表单中使用ngModel已被弃用(并在Angular 7中删除),因此我按照以下方式修改了模板和组件。

模板:

<mat-form-field>
    <mat-select [formControl]="filter" multiple 
                [compareWith]="compareFn">
        <mat-option *ngFor="let v of values" [value]="v">{{v.label}}</mat-option>
    </mat-select>
</mat-form-field>

该组件的主要部分(onChanges和其他详细信息被省略):
interface SelectItem {
    label: string;
    value: any;
}

export class FilterComponent implements OnInit {
    filter = new FormControl();

    @Input
    selected: SelectItem[] = [];

    @Input()
    values: SelectItem[] = [];

    constructor() { }

    ngOnInit() {
        this.filter.setValue(this.selected);
    }

    compareFn(v1: SelectItem, v2: SelectItem): boolean {
        return compareFn(v1, v2);
    }
}

function compareFn(v1: SelectItem, v2: SelectItem): boolean {
    return v1 && v2 ? v1.value === v2.value : v1 === v2;
}

请注意,在上面的ngOnInit中,this.filter.setValue(this.selected)。这在Angular 6中似乎能正常运行。


这实际上应该是最好的答案,因为它还涵盖了在处理两个不同的API结果进行比较时的对象选择。 - Marco Klein
(例如:在另一个API调用中选择的项目和可供选择的项目总列表)。 - Marco Klein
Angular 7仍然支持模板驱动模型!但是您不能在同一模板上混合使用响应式表单。您提供的[compareWith]提示非常好。 - LeO

3
我按照这些示例进行操作,尝试将mat-select的值设置为mat-options之一的值。但是失败了。
我的错误在于将数字类型变量的[(value)]="someNumberVariable"应用到字符串类型的mat-options中。即使它们在模板中看起来相同,也不会选择该选项。
一旦我将someNumberVariable解析为字符串,一切就都好了。
因此,似乎你需要让mat-select和mat-option的值不仅是相同的数字(如果你要呈现数字),而且还要让它们成为字符串类型。

那也是我的问题。一个是数字,另一个是字符串。 - Vadim Berman

2
我的解决方案是:
<mat-form-field>
  <mat-select #monedaSelect  formControlName="monedaDebito" [attr.disabled]="isLoading" [placeholder]="monedaLabel | async ">
  <mat-option *ngFor="let moneda of monedasList" [value]="moneda.id">{{moneda.detalle}}</mat-option>
</mat-select>

TS:

@ViewChild('monedaSelect') public monedaSelect: MatSelect;
this.genericService.getOpciones().subscribe(res => {

  this.monedasList = res;
  this.monedaSelect._onChange(res[0].id);


});

使用对象:{id: 数字, detalle: 字符串}

1
我的解决方案有点巧妙,也更简单。
<div>
    <mat-select
        [placeholder]="selected2">
      <mat-option
          *ngFor="let option of options2"
          value="{{ option.id }}">
        {{ option.name }}
      </mat-option>
    </mat-select>
  </div>

我刚刚使用了占位符。材料占位符的默认颜色是浅灰色。为了使其看起来像选中了该选项,我只需按以下方式操纵CSS:
::ng-deep .mat-select-placeholder {
    color: black;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接