输入更改时 Angular2 动态输入字段失去焦点

Angular2 Dynamic input field lose focus when input changes

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

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>

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

当数组是基本类型时会发生这种情况,在您的例子中是 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,其中 returns 值的(唯一)index

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

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

You are repeating over an array and you are changing the items of the array (note that your items are strings, which are primitives in JS and thus compared "by value"). Since new items are detected, old elements are removed from the DOM and new ones are created (which obviously don't get focus).

使用 TrackBy Angular 可以根据唯一标识符跟踪添加(或删除)了哪些项目,并仅创建或销毁发生变化的事物,这意味着您不会失去注意力您的输入字段:)

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

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

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

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

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 可能需要更改的情况下,需要手动更新,以便更改检测器触发重新渲染。

当您输入一些文本时,angular 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
}