Неожиданное поведение ngModel внутри Angular Form - PullRequest
0 голосов
/ 14 декабря 2018

Следующий код сгенерирует два inputs с одинаковым значением bye (вместо hello и bye).Было бы здорово, если бы кто-то (теоретически) мог объяснить это поведение и сообщить точную причину.

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

enter image description here

Редактировать: Объяснитьмой вопрос лучше:

Причина не может быть в том, что, поскольку они связаны с одним и тем же объектом, они будут иметь одинаковое значение.Если это так, то следующий случай будет иметь одинаковое значение для обоих входов, что, очевидно, не так.

app.component.html

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

app.component.ts

  arr = [1,4]

  ngOnInit(){
    setTimeout(()=>{
      this.arr[1] = 5;
    });
  }

enter image description here

Обратите внимание: Мне кажется, я объяснил свойвопрос правильно, а также почему я думаю, что решение @ DeborahK мне не подходит.Я ищу причину такого поведения.И не обходной путь.Кроме того, я знаю, что изменение name в каждом входе будет работать нормально.Поэтому, пожалуйста, прекратите предлагать это.

Ответы [ 3 ]

0 голосов
/ 14 декабря 2018

Вот еще одно объяснение того, почему ответом должно быть уникальное имя.И это решение - , а не обходное решение.Это просто , как он работает.

Когда вы используете формы на основе шаблонов, которые вы используете при 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

0 голосов
/ 15 декабря 2018

Кажется, что в вашем образце кода есть комбинация двух проблем :

  1. Два входа имеют одинаковое имя, что приводит к тому, что они совместно используют один и тот же FormControl
  2. Каждый элемент ввода удаляется и воссоздается, когда он обновляется при обнаружении изменений.Если другое значение не было изменено, соответствующий элемент ввода не воссоздается.По-видимому, это вызывает десинхронизацию с FormControl, и мы видим разные значения в двух полях.

Чтобы проиллюстрировать последнюю точку, вы можете принудительно воссоздать два входа при обнаружении изменений, изменив обаиз них в коде:

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

Вы можете увидеть в этот стек , что оба входа имеют одинаковое содержимое после обновления.

Уничтожение / создание связанных входных элементов в цикле ngFor можно предотвратить с помощью метода trackBy, чтобы отслеживать элементы массива по их индексу, а не отслеживать их по их значению.Вы можете видеть в этом стеке , что два элемента ввода правильно используют один и тот же 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;
}

Вы можете увидеть этот стек для демонстрации.

Поток данных в управляемых шаблонами формах (модель для просмотра)

Директива ngModel обновляет FormControl при изменении связанных данных путем вызова FormControl.setValue:

Исходный код ngModel :

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код :

patchValue(value: any, options: {
  onlySelf?: boolean,
  emitEvent?: boolean,
  emitModelToViewChange?: boolean,
  emitViewToModelChange?: boolean
} = {}): void {
  this.setValue(value, options);
}
0 голосов
/ 14 декабря 2018

Атрибут имени должен быть уникальным

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