67

我正在制作一个动态表格。AField有一个值列表。每个值都由一个字符串表示。

export class Field{
    name: string;
    values: string[] = [];
    fieldType: string;
    constructor(fieldType: string) {this.fieldType = fieldType;}
}

我的组件中有一个函数,它为该字段添加了一个新值。

addValue(field){
    field.values.push("");
}

值和按钮在我的 HTML 中显示如下。

<div id="dropdown-values" *ngFor="let value of field.values; let j=index">
    <input type="text" class="form-control" [(ngModel)]="field.values[j]" [name]="'value' + j + '.' + i"/><br/>
</div>
<div class="text-center">
    <a href="javascript:void(0);" (click)="addValue(field)"><i class="fa fa-plus-circle" aria-hidden="true"></i></a>
</div>

一旦我在一个值的输入中写入一些文本,输入就会失去焦点。如果我向一个字段添加许多值,并且我在一个值输入中写入一个字符,则输入失去焦点并且该字符被写入每个输入中。

4

3 回答 3

165

当数组是原始类型时会发生这种情况,在您的情况下是String数组。这可以通过使用来解决TrackBy。因此,更改您的模板以匹配以下内容:

<div *ngFor="let value of field.values; let i=index; trackBy:trackByFn">
    <input type="text" [(ngModel)]="field.values[i]"  /><br/>
</div>
<div>
    <button (click)="addValue(field)">Click</button>
</div>

并在 ts 文件中添加函数trackByFn,它返回值的(唯一)index

trackByFn(index: any, item: any) {
   return index;
}

这是关于同一问题的链接,除了问题是针对 AngularJS 的,但问题与您的问题相对应。该页面最重要的摘录:

您正在重复一个数组并且您正在更改数组的项目(请注意,您的项目是字符串,它们是 JS 中的基元,因此“按值”进行比较)。由于检测到新项目,因此从 DOM 中删除旧元素并创建新元素(显然没有获得焦点)。

使用TrackByAngular 可以根据唯一标识符跟踪已添加(或删除)哪些项目,并仅创建或销毁已更改的内容,这意味着您不会失去对输入字段的关注:)

如链接中所见,您还可以修改数组以包含唯一的对象并使用[(ngModel)]="value.id"例如,但这可能不是您需要的。

于 2017-02-19T15:24:49.213 回答
0

当我使用辅助函数迭代对象的键和值时,这发生在我身上:

<div *ngFor="let thing of getThings()" [attr.thingname]="thing.key">
  ... {{ applyThing(thing.value) }}
</div>

在我的组件中,我返回了一个包含键/值对的对象数组:

export ThingComponent {
  ...

  //this.things = { a: { ... }, b: { ... }, c: { ... } }

  public getThings() {
    return Object.keys(this.things).map((key) => {
      return {key: key, value: this.things[key] }
    })
  }
}

@AJT_82 给出的答案肯定与宣传的完全一样。但是,在我的情况下,具体问题是辅助函数getThings()每次都返回一个新的对象列表。即使它们的内容相同,对象本身也会在每次调用函数时重新生成(在更改检测期间发生),因此,对于更改检测器,它们具有不同的身份,并且在每次模型更改时都会重新生成表单。

在我的情况下,简单的解决方案是缓存结果getThings()并将其用作迭代器:

<div *ngFor="let thing of cachedThings" [attr.thingname]="thing.key">
  ... {{ applyThing(thing.value) }}
</div>

...

export ThingComponent {
  public cachedThings = getThings()
  ...

  //this.things = { a: { ... }, b: { ... }, c: { ... } }

  private getThings() {
    return Object.keys(this.things).map((key) => {
      return {key: key, value: this.things[key] }
    })
  }
}

cachedThings可能需要更改的情况下,需要手动更新,以便更改检测器触发重新渲染。

于 2017-08-14T18:24:24.117 回答
0

当您输入一些文本时,角度 ngOnChange 侦听器将运行,因此 ngFor 索引将重新呈现并且焦点将丢失。为了避免这个问题,可以将 trackFn 添加到 ngFor。有关该主题的更多详细信息,请参阅https://angular.io/api/core/TrackByFunction。在您的问题的代码解决方案下方:

在您的 html 代码中定义 ngFor 一个 trackFn 函数,该函数将对应于 .ts 文件中声明的 trackByFn 函数。

    <div *ngFor="let value of field.values; let i=index; trackBy:trackByFn">
    <input type="text" [(ngModel)]="field.values[i]"  /><br/>
</div>
<div>
    <button (click)="addValue(field)">Click</button>
</div>

并在 ts 文件中声明上面提到的 trackByFn 函数:

trackByFn(i: number, items: any) {
return index //returning the index itself for avoiding ngFor to change focus after ngModelChange
}
于 2021-11-30T13:57:11.663 回答