ngModel在Angular Form中的意外行为

时间:2018-12-14 18:23:17

标签: angular angular-ngmodel

以下代码将生成两个具有相同值inputs的{​​{1}}(而不是byehello)。如果有人能够(理论上)解释这种行为并指出确切原因,那将是很好的。

bye

enter image description here

编辑:为了更好地解释我的问题:

原因可能不是因为它们绑定到同一对象,所以它们将具有相同的值。如果是这样,则以下情况对于两个输入都具有相同的值,显然不是这种情况。

app.component.html

<form>
  <div *ngFor="let item of ['hello', 'bye'];">
    <input name="name" [(ngModel)]="item">
  </div>
</form>

app.component.ts

<form>
  <div *ngFor="let item of arr;">
    <input name="name" [(ngModel)]="item">
  </div>
</form>

enter image description here

请注意::我想我已经正确解释了我的问题,以及为什么我认为@DeborahK的解决方案似乎不适合我。我正在寻找这种行为的原因。而不是解决方法。另外,我知道在每个输入中更改 arr = [1,4] ngOnInit(){ setTimeout(()=>{ this.arr[1] = 5; }); } 可以使其正常工作。因此,请不要再建议了。

3 个答案:

答案 0 :(得分:3)

名称属性应唯一

 <form>
      <div *ngFor="let item of ['hello', 'bye'];let i =index">
        <input  name="{{i}}" [(ngModel)]="item">
      </div>
    </form>

答案 1 :(得分:0)

以下是有关此处答案为何要使用唯一名称的进一步说明。而且此解决方案不是 的解决方法。这只是工作方式

当使用模板驱动的表单时(即使用ngModel时),Angular会自动构建一个数据结构来保存所有表单信息。这包括状态信息(脏污,被触摸等)和表单值。它根据“控制名称”保存此信息!

因此,如果您的名字相同,则它们在数据结构中的位置为“一个元素”,因此不能有两个值。

enter image description here

如果您为表单定义模板引用变量,则可以自己查看此数据结构:

<form #myForm="ngForm">
  <div *ngFor="let item of ['hello', 'bye'];">
    <input name="name" [(ngModel)]="item">
  </div>
  <div>{{ myForm.value | json }}</div>
</form>

我刚刚进行了一次堆栈闪电演示,以演示您的数组示例,以表明它仍然只是一个具有一个值的元素:

https://stackblitz.com/edit/angular-xjyslr

答案 2 :(得分:0)

您的code sample中似乎有两个问题的组合:

  1. 两个输入具有相同的名称,这导致它们共享相同的FormControl
  2. 在更改检测中更新每个输入元素时,将删除并重新创建每个输入元素。如果未修改其他值,则不会重新创建相应的输入元素。这似乎导致与FormControl失去同步,并且我们在两个字段中看到了不同的值。

为说明最后一点,您可以通过在代码中修改两个输入来强制在更改检测时重新创建两个输入:

changeValues() {
  this.arr[0] = 2;
  this.arr[1] = 3;
}

您可以在this stackblitz中看到更新后两个输入的内容相同。


可以借助ngFor方法来防止trackBy循环中绑定输入元素的破坏/创建,该方法可以通过索引来跟踪数组元素,而不是通过值来跟踪数组元素。您可以在this stackblitz中看到两个输入元素正确共享同一FormControl。

<div *ngFor="let item of arr; trackBy: trackByFn">
  <input name="name" [ngModel]="item">
</div>
trackByFn(index, value) {
  return index;
}

最后,只需对原始代码进行3次更改即可获得正确的行为:

  1. 给每个输入一个唯一的名称
  2. 使用trackBy方法(按索引)防止输入元素的破坏/创建
  3. 对于双向绑定,请按其索引绑定数组值,而不是绑定到循环变量item
<div *ngFor="let item of arr; let i = index; trackBy: trackByFn">
  <input name="name_{{i}}" [(ngModel)]="arr[i]">
</div>
trackByFn(index, value) {
  return index;
}

您可以查看this stackblitz进行演示。


以模板驱动形式的数据流(从模型到视图)

通过调用ngModel修改绑定数据时,FormControl指令updates FormControl.setValue

ngModel source code

ngOnChanges(changes: SimpleChanges) {
  ...    
  if (isPropertyUpdated(changes, this.viewModel)) {
    this._updateValue(this.model);
    this.viewModel = this.model;
  }
}

private _updateValue(value: any): void {
  resolvedPromise.then(
      () => { this.control.setValue(value, {emitViewToModelChange: false}); });
}

您会看到FormControl.patchValue也呼叫setValue

FormControl source code

patchValue(value: any, options: {
  onlySelf?: boolean,
  emitEvent?: boolean,
  emitModelToViewChange?: boolean,
  emitViewToModelChange?: boolean
} = {}): void {
  this.setValue(value, options);
}