【问题标题】:Angular2 Dynamic input field lose focus when input changes输入更改时Angular2动态输入字段失去焦点
【发布时间】:2017-02-19 02:24:42
【问题描述】:

我正在制作一个动态表格。 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>

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

【问题讨论】:

标签: javascript angular angular2-forms ngfor angular2-ngmodel


【解决方案1】:

当数组是原始类型时会发生这种情况,在您的情况下是 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;
}

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

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

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

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

【讨论】:

  • 在我的例子中,我不是在迭代一个基元数组,而是一个对象数组(实际上是一个 FormArray),我仍然遇到同样的问题。即便如此,我还是按照@AJT_82 的建议解决了问题
  • @AlexisJoséBravoLlovera 很高兴听到它有效并且对您有所帮助! formarray 似乎发生了某种事情,我自己也不知道原因,因为我们确实在处理对象,类似的问题:stackoverflow.com/questions/44445676/…
  • 你先生是个英雄。谢谢。
  • @AJT82 非常感谢!这对我帮助很大,我一直在努力奋斗
【解决方案2】:

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

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

【讨论】:

    【解决方案3】:

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

    【讨论】:

      猜你喜欢
      • 2022-01-19
      • 2019-10-21
      • 2018-07-12
      • 2017-09-17
      • 1970-01-01
      • 1970-01-01
      • 2021-12-14
      相关资源
      最近更新 更多