Двухстороннее связывание для элементов Array с использованием <form> - PullRequest
0 голосов
/ 19 февраля 2019

У меня есть массив объектов в компоненте.который я буду повторять в шаблоне.

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 голосов
/ 19 февраля 2019

Вы можете решить вашу проблему, используя функцию «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>

Пожалуйста, дайте мне знать, если это решение работает для вас!

0 голосов
/ 19 февраля 2019

Создайте другую переменную и итерируйте эту переменную для ввода входных данных

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);
  }
}

Рабочая демонстрация

0 голосов
/ 19 февраля 2019

Я полностью подозреваю, что это происходит из-за конфликта в значении атрибута 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: я исследую, почему это работает иначе, когда есть комбинация name и inputЯ подозреваю о проводке API form с элементом input.Я обновлю ответ, как только получу что-нибудь.

0 голосов
/ 19 февраля 2019

Проблема в том, что вы используете форму.Поскольку вы используете форму, вам нужно указать, как angular должен отслеживать изменения для ваших элементов формы, если вы планируете изменить существующего источника.Вы можете сделать это, используя trackBy pipe:

<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

...