Угловой: обработка изменений объекта в ngFor, принудительная печать

#arrays #angular #typescript #ngfor #ngonchanges

Вопрос:

У меня есть свой app.component со списком объектов

 class Hero {
    alias: string;

    constructor(public firstName: string,
                public lastName: string) {
    }
}

class AppComponent {
...
heroes: Hero[] = [
    new Hero("foo", "bar")
];
...
onHeroChange($event: Hero, index: number): void {
    this.heroes[index] = $event;
}
 
 
<div *ngFor="let hero of heroes; let index=index">
  <hero [hero]="hero" (heroChange)="onHeroChange($event, index)"></hero>
</div>

 

ГероКомпонент-это

 export class HeroComponent {
  @Input()
  set hero(newValue: Hero) {
    this._hero = newValue;
    this.showAlias = !!newValue.alias;
  }

  get hero(): Hero {
    return this._hero;
  }

  @Output() heroChange: EventEmitter<Hero> = new EventEmitter<Hero>();
  showAlias: boolean = !1;

  private _hero: Hero;

  //

  @HostListener('click')
  onHeroClick(): void {
    this.hero.alias = `alias_${ new Date()}`;
    console.info('HERO TO EMIT', this.hero);
    this.heroChange.emit(this.hero);
  }
}
 

Моя проблема в том , что даже при назначении измененного героя внутри app.component , set hero внутренняя hero.component часть не вызывается, поэтому showAlias в примере не обновляется, и я не вижу псевдонима в hero.component .

Нужно ли мне форсировать ngFor, назначив весь массив?

Может быть, обходным путем можно было бы удалить объект из массива, а затем вставить его снова? Хотя это звучит как бесполезное вычисление.

Примечание: это всего лишь пример, это не то, над чем я действительно работаю, так что что-то вроде

Обновите реквизит showAlias в методе onHeroClick

или

Назначьте героя в компоненте hero.

к сожалению, это не решает проблему. Мне нужно, чтобы изменения происходили снаружи, потому что происходят другие вещи.

Может быть еще один вариант изменения обнаружения onPush и маркировки для проверки вручную?

Блиц ==> >https://stackblitz.com/edit/angular-ivy-kpn3ds

Ответ №1:

Вы не устанавливаете новое hero , вы просто изменяете свойство существующего:

 this.hero.alias = `alias_${ new Date()}`;
 

Это не увольняет сеттера. Измените строку вот так:

 this.hero = {...this.hero, alias: `alias_${ new Date()}`};
 

Комментарии:

1. Спасибо, но я уже упоминал, что это ничего не решит. Изменение должно быть отражено в массиве, это приведет к слишком раннему запуску сеттера внутри компонента hero.component.

2. Возможно, я ошибаюсь, но мне кажется, что вы ничего не задаете. С чего бы это увольнять сеттера? Вы изменяете hero объект HeroComponent , испускаете тот же объект AppComponent , которому снова передаете тот же объект HeroComponent . Ссылка не изменилась, сеттер не вызывался. Почему вы ожидаете чего-то другого?

3. Если вы посмотрите на app.component него, то увидите, что там назначен новый герой. И я не ожидаю, что сеттер выстрелит. Я прошу чистого решения, чтобы снова запустить ngFor для печати.

4. Это не новый герой. Это ссылка на того же самого старого (только что измененного) героя, исходящего от HeroComponent . Снова начинается ngFor печать, но герой, который HeroComponent появляется в этом новом ngFor цикле, тот же, что и раньше. Для обнаружения угловых изменений по умолчанию используется ссылка. Чтобы изменить ссылку в AppComponent, вы можете сделать это: this.heroes[index] = {...$event};

5. В зависимости от сложности вашего реального варианта использования другим решением было бы определить отдельный компонент для псевдонима. Поскольку это будет связано со alias свойством (а не со всем объектом), обнаружение изменений будет работать без необходимости рассматривать hero как неизменяемое: https://stackblitz.com/edit/angular-ivy-yhpv3j