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

时间:2017-02-19 02:24:43

标签: javascript jquery html angular angular2-forms

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

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

3 个答案:

答案 0 :(得分:82)

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

这是关于同一问题的Lazy-loading without proxies,除了问题是针对AngularJS,但问题与你的问题相对应。最重要的摘录自该页面:

  

你正在重复一个数组而你正在改变数组的项目(注意你的项目是字符串,它们是JS中的原语,因此比较&#34;按值&#34;)。由于检测到新项目,因此从DOM中删除旧元素并创建新元素(显然无法获得焦点)。

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

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

答案 1 :(得分:1)

此库解决了此类问题:
https://www.npmjs.com/package/keyboard-navigator
通过trackby保持焦点仅适用于迭代元素,并且如果该元素确实发生了更改(唯一标识符的更改),您的焦点也会丢失。 在少数使用store(ngrx / redux)的应用程序中,可能存在一个根对象,而相关列表可能是该根对象的子子属性,在大多数情况下,reducer会复制根对象(丢失旧引用) )也会在所需DOM节点以及其他不需要的节点上触发DOM更新。 我发现的另一种选择是通过Xpath参考来保持焦点。

考虑一个函数retainFocus,该函数在被调用时会存储当前活动元素的Xpath并触发一个异步函数,该函数会将元素集中在与上面保存的Xpath匹配的DOM上。

现在在触发DOM更新的store-update-subscription / template-bindings内部调用此函数可确保相同位置上的元素将被聚焦(它可能是同一项目,也可能是其他但集中的元素)相对位置。)

答案 2 :(得分: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可能需要更改,则需要手动更新,以便更改检测器触发重新呈现。