使用<form>在Array元素上的两种方式绑定

时间:2019-02-19 16:39:41

标签: javascript angular angular-components angular-template

我在组件中有一组对象。我将在模板中进行迭代。

app.component.ts

import {Component, OnInit} from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  title = 'sample-app';
  classesData = [];

  constructor() {
  }

  ngOnInit() {
    this.classesData = [
      {title: 'Hello0'}, {title: 'Hello1'}, {title: 'Hello2'}
    ];
  }

  duplicate() {
    const newData = JSON.parse(JSON.stringify(this.classesData[1]));
    newData.title += 'Copy';
    this.classesData.splice(1, 0, newData);
  }
}

app.template.html

<form #testingFrom="ngForm">
  <p>{{classesData | json}}</p>
  <div *ngFor="let classData of classesData; let i=index">
    <input [(ngModel)]="classData.title" name="{{'title-' + i}}" type="text">
  </div>
  <button (click)="duplicate()">Duplicate</button>
</form>

我的目标是当用户单击重复按钮时,我只是在数组的 index 1 中添加了一个新元素。我的初始状态看起来像(用户点击之前)

enter image description here

用户单击重复按钮后的状态

enter image description here

在上方第3个输入字段的图像中,我们得到的是 Hello1Copy ,而不是 Hello1

4 个答案:

答案 0 :(得分:1)

问题是您正在使用表单。由于您使用的是表格,因此,如果您打算更改现有数据源,则需要指定角度应如何跟踪表格项的更改。您可以使用trackBy管道执行此操作:

<form #testingFrom="ngForm">
  <p>{{classesData | json}}</p>
  <div *ngFor="let classData of classesData; let i=index; trackBy: trackByFn">
    <input [(ngModel)]="classData.title" [name]="'title-' + i" type="text">
  </div>
  <button (click)="duplicate()">Duplicate</button>
</form>

打字稿相关部分:

  trackByFn(index: any) {
    return index;
  }

请注意,将添加元素添加到集合中将在您的原始示例中起作用。

有效的堆叠闪电战:https://stackblitz.com/edit/angular-uabuya

答案 1 :(得分:1)

我完全怀疑这种行为是由于name属性值中的冲突而发生的。仅在这种情况下,如果您在第一个位置splice newItem,它只会添加该变量,而其他DOM不会重新呈现。对于交叉验证,您可以尝试将input元素替换为{{classData.title}}这样的简单绑定,然后一切正常。

可以通过始终不冲突name属性值来轻松解决此问题。这意味着为每个收集项分配一个唯一的id变量并使用它。

this.classesData = [
  { id: 1, title: 'Hello0' }, 
  { id: 2, title: 'Hello1' }, 
  { id: 3, title: 'Hello2' }
];
duplicate() {
    const newData = JSON.parse(JSON.stringify(this.classesData[1]));
    newData.title += 'Copy';
    newData.id = Date.now()
    this.classesData.splice(1, 0, newData);
}

模板

<div *ngFor="let classData of classesData;let i=index">
   <input [(ngModel)]="classData.title" [name]="'title_'+classData.id" type="text">
</div>

Stackblitz


您还可以通过从每个输入字段中删除name属性来验证相同内容。但这还不够,它会抛出

  

错误错误:如果在表单标签中使用ngModel,则名称   必须设置属性或将表单控件定义为   ngModelOptions中的“独立”。

因此,在每个输入字段上添加[ngModelOptions]="{standalone: true}",以使没有name属性的输入正常工作。如@briosheje的另一个答案所建议,您还可以使用trackBy来强制执行渲染。

PS:我正在研究为什么在nameinput组合使用时,这种方法为何工作不同的原因,我怀疑使用form元素的input API接线。知道后,我会立即更新答案。

答案 2 :(得分:0)

制作另一个变量,然后对该变量进行迭代以创建输入框

import { Component,OnInit } from '@angular/core';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent implements OnInit {
  title = 'sample-app';
  originalData=[];
  classesData = [];

  constructor() {
  }

  ngOnInit() {
    this.classesData = [
      {title: 'Hello0'}, {title: 'Hello1'}, {title: 'Hello2'}
    ];
    this.originalData=[...this.classesData]; // changed here
  }

  duplicate() {
    const newData = JSON.parse(JSON.stringify(this.classesData[1]));
    newData.title += 'Copy';
    this.classesData.splice(1, 0, newData);
  }
}

Working Demo

答案 3 :(得分:0)

您可以使用“ trackBy”功能解决问题。请参见下面的代码示例。

app.component.ts

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
  title = 'sample-app';
  classesData = [];

  constructor() {}

  ngOnInit() {
    this.classesData = [
      { title: 'Hello0' },
      { title: 'Hello1' },
      { title: 'Hello2' }
    ];
  }

  duplicate() {
    const newData = JSON.parse(JSON.stringify(this.classesData[1]));
    newData.title += 'Copy';
    this.classesData.splice(1, 0, newData);
  }

  trackByIndex(index: number, obj: any): any {
    return index;
  }
}

app.component.html

<form>
  <p>{{classesData | json}}</p>
  <div *ngFor="let classData of classesData; let i=index;trackBy:trackByIndex;">
    <input [(ngModel)]="classesData[i].title" name="{{'title-' + i}}" type="text" />
  </div>
  <button (click)="duplicate()">Duplicate</button>
</form>

请让我知道该解决方案是否适合您!