Изменение @Input() в свойствах дочернего компонента предотвращает дальнейшие обновления родительского компонента.

У меня есть 2 компонента - Parent и Child. См. демо stackblitz родительский шаблон выглядит следующим образом: введите здесь описание изображения

Когда нажимается «Показать дочерний элемент», для параметра isChild1Visible устанавливается значение true, в результате чего дочерний компонент отображает свой шаблон. введите здесь описание изображения

Когда вы нажимаете «Скрыть меня», дочерний компонент устанавливает для isVisible значение false, в результате чего он скрывается. Теперь при повторном нажатии «Показать дочерний элемент» дочерний компонент не отображается.

Я добавляю ngOnChanges() в дочерний компонент, чтобы увидеть обнаружение изменений, однако я вижу следующее. Когда дочерний компонент инициализирован: введите здесь описание изображения

После первого нажатия кнопки «Показать» введите здесь описание изображения

После нажатия кнопки «Скрыть меня» ngOnChanges() ничего не печатает, даже если позже будет нажата кнопка «Показать меня».

Итак, почему обнаружение изменений перестает работать после обновления входного свойства в дочернем компоненте?


person Phalgun    schedule 22.07.2021    source источник
comment
Вы должны использовать двустороннюю привязку, чтобы родительское значение и дочернее значение быть в синхронизации.   -  person PsyGik    schedule 22.07.2021
comment
попробуйте передать объект вместо примитивного типа   -  person Hamza    schedule 22.07.2021


Ответы (3)


The issue

В первый раз, когда вы устанавливаете isChild1Visible, с односторонней привязкой он также устанавливает дочерний элемент isVisible в значение true.

Изнутри вы устанавливаете isVisible в false, поэтому div исчезает, как у вас есть *ngIf="isVisible".

Однако внешний isVisible по-прежнему true!

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

См. этот демонстрация stackblitz с открытыми обоими логическими значениями.

Option 1: Explicitly listen to on output

Если вы хотите управлять компонентом извне, вы должны оставить логику снаружи.

Возможный способ - добавить @Output к вашему дочернему элементу, который является EventEmitter, который испускается, когда вы хотите закрыть дочерний элемент.

Родитель прослушает это событие и сделает что-нибудь, например установит для isVisible значение false.

Вы можете увидеть это на этом обновленном stackblitz.

Option 2: Two-way binding

Как и в варианте решения 1, вы можете создать эмиттер с тем же именем, что и ваш ввод, заканчивающимся на Change, и вызвать там свое изменение.

В этом случае вам нужно только заменить привязку на [(isVisible)]="isChild1Visible" и добавить isVisibleChange как @Output EventEmitter<any>.

Демонстрация на stackblitz.

person Balastrong    schedule 22.07.2021
comment
Да, я использовал @Output() для решения проблемы, но вопрос в том, почему это не работает так, как я продемонстрировал в вопросе. - person Phalgun; 22.07.2021
comment
Поскольку в первый раз, когда вы устанавливаете для дочернего элемента isVisible значение true извне, затем вы устанавливаете для него значение false из дочернего элемента (с внешним все еще истинным), и дальнейшие изменения не обнаруживаются. В конце концов, нет никаких изменений для обнаружения, поскольку внешняя переменная никогда не менялась после того, как вы ее установили true Я обновляю ответ. - person Balastrong; 22.07.2021
comment
Ответ обновлен демоверсией! :) - person Balastrong; 22.07.2021
comment
с точки зрения родительского компонента входное значение не изменилось, поэтому нет необходимости обновлять его в дочернем компоненте - person iamaword; 22.07.2021

Поскольку данные isChild1Visible передаются как примитивный тип (логический), они будут передаваться по значению.

Следовательно, при передаче объекта, массива и т.п. он передается по ссылке, а для примитивных типов, таких как числа, передается по значению.

попробуйте это без добавления каких-либо дополнительных выходов:

Компонент MyAppChild1

export class MyAppChild1Component implements OnInit {
  @Input() isVisible;
  constructor() {}

  ngOnInit() {}
  onClick() {
    this.isVisible.value = false;
  }
}

<div *ngIf="isVisible.value">
  <span>
Child
</span>
  <button (click)="onClick()">Hide me</button>
</div>

Компонент приложения

export class AppComponent {
  isChild1Visible = { value: false };
  ngOnInit() {}
  onShowChild() {
    this.isChild1Visible.value = true;
  }
}

<div>
  <p>Parent</p>
  <button (click)="onShowChild()">show Child</button>
  <app-my-app-child1 [isVisible]="isChild1Visible"></app-my-app-child1>
</div>
person Hamza    schedule 22.07.2021
comment
Мне нравится ваш ответ @Hamza: Возможно, этот подход не является лучшей практикой, но я думаю, что довольно интересно знать о таких вещах. Спасибо! - person Juan Berzosa Tejero; 23.07.2021

Это происходит потому, что isChild1Visible в app.component не обновляется, когда дочерний компонент устанавливает isVisible.

First Click on onShowChild()

  • isChild1Visible = правда
  • isVisible = правда

Second Click on onClick() in child

  • isVisible = ложь;
  • isChild1Visible = правда

Second Click on onShowChild()

  • isChild1Visible = true // => Здесь нет изменений
  • isVisible = ложь

Поскольку значение isChild1Visible не меняется, Angular ничего не обновляет.

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

<app-my-app-child1 [(isVisible)]="isChild1Visible"></app-my-app-child1>
export class MyAppChild1Component implements OnInit {
  @Input() isVisible;
  @Output() isVisibleChange = new EventEmitter<boolean>();

  constructor() {}

  ngOnInit() {}
  onClick() {
    this.isVisibleChange.emit(!this.isVisibleChange);
  }
}

Обновленная песочница

person PsyGik    schedule 22.07.2021