Значение обновления ngModel не отражается внутри компонента - PullRequest
0 голосов
/ 06 июня 2018

У меня есть компонент с реализацией ControlValueAccessor, но когда связанное значение изменяется вне компонента, оно не обновляется корректно.

РЕДАКТИРОВАТЬ: Пример Stackblitz: https://stackblitz.com/edit/angular-lmojnj

Я объясню сенарио.В начале есть два изображения в массиве, если я щелкну одно из изображений, оно вызовет toggleSelected сортировщика изображений, который обновит tempImages, чтобы иметь только 1 изображение (то, которое не было нажато).Затем, если я введу URL-адрес изображения на вход parent.component и нажму кнопку ввода, он вызовет addImage parent.component, который увеличит URL до tempImages и, следовательно, должен выполнить логику в сортировщике изображений (Должен ли вызывать writeValue автоматически вызвать изменение значения? - и это не так).Теперь, если я щелкну назад на том же изображении, которое щелкнуло ранее (или на другом выбранном изображении), появится новое изображение, которое я добавил ранее!Он должен появиться, когда я добавлю его в массив tempImages, потому что это двусторонняя привязка, не так ли?

Чтобы решить эту проблему, я добавил ссылку на сортировщик изображений (<app-images-sorter2 #imagesSorter [(ngModel)]="tempImages"></app-images-sorter2>), а затем передал ссылкудобавитьImage и вызвать imageSorterCompoennt.writeValue(this.tempImages);, которые обновляют то, что я ожидаю, но это плохая практика и неправильное решение, насколько я знаю

images-sorter2.component.ts:

import { Component, forwardRef, ViewEncapsulation } from '@angular/core';
import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms';

@Component({
  selector: 'app-images-sorter2',
  templateUrl: './images-sorter2.component.html',
  styleUrls: ['./images-sorter2.component.scss'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => ImagesSorter2Component),
      multi: true
    }
  ],
  encapsulation: ViewEncapsulation.None
})
export class ImagesSorter2Component implements ControlValueAccessor {
  public allImages: string[] = [];
  public images: string[] = [];

  private onChange: (_: any) => void = () => {};

  constructor() {}

  public writeValue(value: string[]): void {
    this.images = value || [];
    this.images.forEach((image) => {
      if (!this.allImages.includes(image)) {
        this.allImages.push(image);
      }
    });
    this.onChange(this.images);
  }

  public registerOnChange(fn: (_: any) => void): void {
    this.onChange = fn;
  }

  public registerOnTouched(): void {}

  public isSelected(image: string): boolean {
    return this.images.includes(image);
  }

  public getImageModelIndex(image: string): number {
    return this.images.indexOf(image);
  }

  public toggleSelected(image: string): void {
    const indexOfImage = this.getImageModelIndex(image);
    let newValue;
    if (indexOfImage > -1) {
      newValue = [...this.images.slice(0, indexOfImage), ...this.images.slice(indexOfImage + 1)];
    } else {
      newValue = [...this.images, image];
    }
    this.writeValue(newValue);
  }
}

images-sorter2.component.html:

<div fxLayout="row wrap">
  <div *ngFor="let image of allImages"
       class="image-wrapper"
       [ngClass]="{'selected': isSelected(image)}"
       (click)="toggleSelected(image)">
    <div class="index-container">
      <div>{{ getImageModelIndex(image)+1 }}</div>
    </div>
    <div class="image-container">
      <img [src]="image" />
    </div>
  </div>
</div>

parent.component.ts:

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

@Component({
  selector: 'app-parent',
  templateUrl: './parent.component.html',
  styleUrls: ['./parent.component.scss'],
  encapsulation: ViewEncapsulation.None
})
export class ParentComponent {
  public tempImages = [
    'https://upload.wikimedia.org/wikipedia/commons/thumb/a/a5/Google_Chrome_icon_%28September_2014%29.svg/1200px-Google_Chrome_icon_%28September_2014%29.svg.png',
    'https://upload.wikimedia.org/wikipedia/commons/thumb/f/fa/Apple_logo_black.svg/1200px-Apple_logo_black.svg.png'
  ];
  public newImage: string = '';

  public addImage(): void {
    this.tempImages.push(this.newImage);
    this.newImage = '';
  }
}

parent.component.html:

<app-images-sorter2 [(ngModel)]="tempImages"></app-images-sorter2>
  <div>
    <mat-form-field>
      <input matInput
             type="text"
             [(ngModel)]="newImage"
             (keyup.enter)="addImage()"
             placeholder="Image URL">
    </mat-form-field>
  </div>

Ответы [ 2 ]

0 голосов
/ 12 июня 2018

У меня была эта проблема раньше, и кажется, что у ControlValueAccesor есть проблемы с уведомлением об изменениях коллекций, поэтому вам нужно переназначить массив.

Для вашего примера строка 18 изменения в файле parent.ts из

this.tempImages.push(this.newImage);

до

this.tempImages = this.tempImages.concat([this.newImage]);

и все должно работать нормально.

0 голосов
/ 12 июня 2018

Нет функции includes на string[], как показывает stckblitz.

Согласно docs она должна работать, но после замены на .indexOf(image) === -1 она работает, Пример

...