简体   繁体   English

在没有 trackBy 的情况下将 mat-select 与 ngFor 一起使用时浏览器冻结

[英]Browser freezes when using mat-select with ngFor without trackBy

TL;DR : I already have working solutions, but I would like explanations why mat-select behaves like this. TL;DR :我已经有了可行的解决方案,但我想解释为什么 mat-select 的行为是这样的。

While building an application with Angular Material I run into this error when using mat-select combined with *ngFor in the template: the get priorities function is being called continuously like in an infinite loop and the browser freezes.在使用 Angular Material 构建应用程序时,我在模板中结合使用mat-select*ngFor时遇到了这个错误: get priorities函数像在无限循环中一样被连续调用,并且浏览器冻结。

Component成分

get priorities() {
    return [
        { name: 'NORMAL', value: 100 },
        { name: 'HIGH', value: 200 },
        { name: 'FORCE', value: 300 },
    ];
}
<mat-form-field appearance="outline">
  <mat-label>Priority</mat-label>
  <mat-select formControlName="priority">
    <mat-option *ngFor="let element of priorities" [value]="element"
      >{{ element.name }}</mat-option
    >
  </mat-select>
</mat-form-field>

Posible solutions可能的解决方案

  1. Specifying each mat-option (without ngFor).指定每个 mat-option(不带 ngFor)。 Although this is not useful for many cases.虽然这在很多情况下没有用。
<mat-form-field appearance="outline">
  <mat-label>Priority</mat-label>
  <mat-select formControlName="priority">
    <mat-option [value]="priorities[0]">{{ priorities[0].name }}</mat-option>
    <mat-option [value]="priorities[1]">{{ priorities[1].name }}</mat-option>
    <mat-option [value]="priorities[2]">{{ priorities[2].name }}</mat-option>
  </mat-select>
</mat-form-field>
  1. Using native html select and option.使用本机 html 选择和选项。 This is a valid solution, but I wanted to make it work with Material.这是一个有效的解决方案,但我想让它与 Material 一起使用。
<select matNativeControl>
  <option *ngFor="let element of priorities" [value]="element">
    {{ element.name }}
  </option>
</select>
  1. It seems that in order to use mat-option with ngFor we need to add a trackBy function.似乎为了将mat-optionngFor一起使用,我们需要添加一个trackBy函数。 ¿Can someone explain why? ¿有人可以解释为什么吗? I am aware that using trackby improves efficiency, but I have not found any Material documentation about why it is necessary to use it with mat-select .我知道使用trackby可以提高效率,但是我还没有找到任何关于为什么需要将它与mat-select一起使用的 Material 文档。
<mat-form-field appearance="outline">
  <mat-label>Priority</mat-label>
  <mat-select formControlName="priority">
    <mat-option
      *ngFor="let element of priorities; trackBy: prioritiesTrackByFn"
      [value]="element"
      >{{ element.name }}</mat-option
    >
  </mat-select>
</mat-form-field>
prioritiesTrackByFn(index, item): number {
    return item.value;
}

trackBy used for detecting changes in item and not redraw all items trackBy 用于检测项目的变化而不是重绘所有项目

trackBy hepl to update only 1 item, without trackBy angular will redraw all items (if var is array of objects) trackBy hepl 仅更新 1 个项目,没有 trackBy angular 将重绘所有项目(如果 var 是对象数组)

Why use a getter?为什么要使用吸气剂? Have you tried to assign the priorities to a public variable in your .ts file?您是否尝试将优先级分配给 .ts 文件中的公共变量?

And of course make use of trackBy:function, as described here: How to use `trackBy` with `ngFor`当然,请使用 trackBy:function,如下所述: How to use `trackBy` with `ngFor`

like:喜欢:

 export class xy { prios: PrioType[]; constructor() { this.prios = getPrios(); } trackById(index, item) { return item.id; } }
 <map-option *ngFor="let element of prios; trackBy:trackById">{{element.name}}</mat-option>

This is how Angular change detection works, it has nothing to do with Angular material.这就是 Angular 变化检测的工作原理,它与 Angular 材质无关。 Angular can run change detection for example when you click the page, when you type something in an input.例如,当您单击页面时,当您在输入中键入内容时,Angular 可以运行更改检测。 Angular does not necessary know WHAT changed, so it checks everything, including your getter. Angular 不需要知道发生了什么变化,因此它会检查所有内容,包括您的 getter。 And as your getter is called, the whole reference of the array is changed (you return a new array each time when getter is called), your array is re rendered and then Angular detects that the array changed, it will run change detection again.... So it kinda becomes a "loop" even though it technically isn't an infinite loop.当你的 getter 被调用时,数组的整个引用被改变(每次调用 getter 时你都会返回一个新数组),你的数组被重新渲染,然后 Angular 检测到数组发生了变化,它将再次运行更改检测。 ......所以它有点成为一个“循环”,即使它在技术上不是一个无限循环。 Being an *ngFor, it just becomes worse.作为一个*ngFor,它只会变得更糟。

This is worth a read regarding angular change detection: https://blog.angular-university.io/how-does-angular-2-change-detection-really-work/关于角度变化检测,这值得一读: https : //blog.angular-university.io/how-does-angular-2-change-detection-really-work/

The simple solution here is to assign your array to a variable and iterate that array in your template instead of using the getter.这里的简单解决方案是将数组分配给一个变量并在模板中迭代该数组,而不是使用 getter。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM