当从 Typescript getter 初始化输入数组时迭代新对象时, Angular Material 按钮不可单击

标签 angular angular-material

我有以下令人困惑的 Angular 问题。

编辑:我设法在 Stackblitz 示例中重现了这一点,并在此处更新了文本。

Stackblitz 示例 here .

这显示了四组带有删除图标的三个列表条目,具有以下配对:

string input/passed from a getter
string input/passed from a field
array input/passed from a getter
array input/passed from a field

在数组/getter 的情况下,当我点击第一个(来自items1)时,没有任何内容被记录到控制台;但是单击第二个(来自 items2)或第三个(来自 items3),它会记录 'delete'。在其他情况下,它总是记录删除,如预期的那样。

这里可能发生了什么?

代码如下,尽管 Stackblitz很容易玩。

首先是父级HTML,它设置了四种情况:

String with getter
<hello [name]="name1">
</hello>
<hr/>
String without getter
<hello [name]="name2">
</hello>
<hr/>
Array with getter
<hello [names]="names1">
</hello>
<hr/>
Array without getter
<hello [names]="names2">
</hello>

和 typescript :

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent {
  get name1(): string {
    return 'Angular';
  }
  name2 = 'Angular';
  get names1(): string[] {
    return ['Angular'];
  }
  names2 = ['Angular'];
}

然后是组件 HTML:

<mat-list>
  <ng-container *ngFor="let item of items1">
    <mat-list-item>
      <h2 mat-line>Some Text</h2>
      <button mat-icon-button (click)="delete()"><mat-icon>delete</mat-icon></button>
    </mat-list-item>
  </ng-container>
  <ng-container *ngFor="let item of items2">
    <mat-list-item>
      <h2 mat-line>Some Text</h2>
      <button mat-icon-button (click)="delete()"><mat-icon>delete</mat-icon></button>
    </mat-list-item>
  </ng-container>
  <ng-container *ngFor="let item of items3">
    <mat-list-item>
      <h2 mat-line>Some Text</h2>
      <button mat-icon-button (click)="delete()"><mat-icon>delete</mat-icon></button>
    </mat-list-item>
  </ng-container>
</mat-list>

和 typescript :

@Component({
  selector: 'hello',
  templateUrl: './hello.component.html',
  styles: [`h1 { font-family: Lato; }`]
})
export class HelloComponent  {
  @Input() name: string;
  @Input() names: string[];

  items1: Number[] = [];
  items2: Number[] = [];
  items3 = [new Number(42)];

  ngOnChanges() {
    this.items1 = [new Number(42)];
    this.items2 = [42];
  }

  delete() {
    console.log('delete');
  }
}

最佳答案

这里的一个问题(尽管不是根本原因)是当使用 ngFor 循环时,Angular 使用对象标识来跟踪它是否需要重新渲染。查看docs供讨论。因此(见下文)添加一个 trackBy 函数解决了这个问题。

这里的坏情况是:

  • 将 getter 用于列表的输入会导致 ngOnChanges 被调用两次,并具有相同的值。大概 Angular 访问输入两次(我不知道为什么),但是当它访问时,getter 每次都返回一个新列表,它具有不同的标识,因此 ngOnChanges 被调用两次。<
  • 不会使用string getter 调用两次; string 对象是相同的。
  • 调用 ngOnChanges 时,它会重置错误列表 (items1) 以包含新对象。

结果是,在这种情况下,Angular 渲染列表两次,因为它认为所有元素从一次传递到下一次都发生了变化。

现在:我不知道为什么这会导致按钮不可点击。但是 Angular 允许您覆盖 trackBy 函数,它用于确定列表的元素是否已更改。并将其更改为按索引进行跟踪(此处未更改)可以解决此问题。

fork Stackblitz example解决问题,它补充说:

到组件的 Typescript:

trackByIndex(index, item) {
  return index;
}

在 HTML 循环中:

<ng-container *ngFor="let item of items1; trackBy: trackByIndex">
  <mat-list-item>
    <h2 mat-line>Some Text</h2>
    <button mat-icon-button (click)="delete()"><mat-icon>delete</mat-icon></button>
  </mat-list-item>
</ng-container>

关于当从 Typescript getter 初始化输入数组时迭代新对象时, Angular Material 按钮不可单击,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56203596/

相关文章:

css - Angular Material 表多粘行

angular - 如何使用 Angular 7 在 mat-select-trigger 标签内显示选定的选项名称

javascript - 错误引用错误: React is not defined | Using React Components in Angular

javascript - 为什么我无法在 Ionic 4 中创建 radio 警报?

angular - 如何在ionic 2中制作表格

node.js - 在同一 POST 请求上使用 multer (NodeJS) + 其他表单数据上传文件 (Angular)

javascript - Angular Material : Get value out of form

Angular Material 日期选择器 : Date Parsing UTC problem 1 day before

angular - 错误 : Failed: Template parse errors: 'mat-checkbox' is not a known element

angular - cdkDragMoved 事件返回基于 clientX 和 clientY 而不是容器的指针位置