Дочерний метод ngOnChanges не запускается при попытке передать новый объект

#angular #typescript #ngonchanges

#angular #typescript #ngonchanges

Вопрос:

У меня есть родительская страница, которая передает user данные дочернему компоненту следующим образом:

 <ng-container *ngIf="leaderboard">
    <app-leaderboard-preview [user]="user" (click)="goToLeaderboard()"></app-leaderboard-preview>
</ng-container>
  

Родительская страница ngOnInit() подписывается на observable, который возвращает пользовательский объект и устанавливает user переменную (я удалил другие нерелевантные запросы из combineLatest ):

 combineLatest([this.userQuery$]).subscribe((results) => {
  Promise.all([this.parseUser(results[4])])
})
  

Насколько я понимаю, он ngOnChanges() не сработает, если не будет создан новый объект, поэтому я назначаю новый пользовательский объект в качестве нового объекта для передачи app-leaderboard-preview компоненту с помощью Object.assign()

   parseUser(user) {
    return new Promise((resolve) => {
      if(user) {
        this.user = Object.assign({}, user);
        resolve(user);
      } else {
        resolve(user);
      }
    })
  }
  

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

   doRefresh(event) {
    if (this.user) {
      //user
      this.userQuery$ = this.db.query$(`user/find?id=${this.user.id}`);
      combineLatest([this.userQuery$]).subscribe((results) => {
        Promise.all([this.parseUser(results[4])])
      })
  }
  

Который затем запускает parseUser метод для обновления user объекта, который app-leaderboard-preview использует.

Таким образом, это должно вызвать ngOnChanges, потому что я передаю «новый» объект компоненту. Что я упускаю?

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

1. Где находится код, который вы переназначаете this.user

2. @RezaRahmati this.user переназначается при каждом запуске parseUser() метода, который размещен в третьем блоке кода

3. Можете ли вы поделиться этим кодом, поскольку он здесь не представлен

4. @RezaRahmati Это 3-й блок кода в его вопросе.

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

Ответ №1:

Редактировать

После некоторого перехода туда и обратно Джордан выяснил, что у него был асинхронный канал, назначенный переменной user, которая переопределяла его изменения для this.user.

 *ngIf = user$ | async as user
  

Оригинальный ответ

Object.assign({}, объект) должен работать. Протестировано на Angular 7.

 this.user = Object.assign({}, user);
  

.. или, если вы используете Lodash, вы можете сделать.

 this.user = _.clone(user);
this.user = _.cloneDeep(user);
  

https://lodash.com/docs/4.17.11#clone

https://lodash.com/docs/4.17.11#cloneDeep

Демонстрация Stackblitz:https://stackblitz.com/edit/ngonchangeswithobject

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

1. спасибо, что помогли мне отладить это. Что-то определенно не так для меня, потому что я только что создал совершенно отдельный метод для анализа пользователя при пролистывании вниз для обновления, называемый `parseUser1(). Я установил user равным null в этом методе, я пробовал как getters, так и setters и ngOnChanges(), и компонент ничего не перехватил после инициализации. Если у вас закончились идеи, не беспокойтесь, я продолжу тестировать новые вещи

2. Извините, я забыл упомянуть. Этот метод запускает ngOnChanges().

3. Хм .. какую версию angular вы используете?

4. У меня есть два входа в моем компоненте, user и rank. Я попытался использовать Object.create() для ввода ранга, и он правильно запустил ngonchanges. Вопрос, который я опубликовал здесь, касается пользовательского ввода, и я попытался использовать Object.create(user) для запуска ngonchanges пользовательского ввода, но по какой-то причине он не запускается, хотя я передаю новый объект на вход. Имеет ли это больше смысла? Извините за путаницу :/

5. Серьезно, большое спасибо, что обратили на это внимание. В итоге я разобрался с этим, и это было действительно глупо. У меня есть ng-container с асинхронностью *ngIf = user$ | async as user , внутри которой находится компонент. Это асинхронное user присвоение переменной в контейнере html имело приоритет над тем, user которое я назначал в своем родительском компоненте. Я очень сожалею, что отнял у вас время, но благодаря вашей помощи я смог найти проблему.

Ответ №2:

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

Способ, которым я обошел это, заключался в использовании методов get() set() для моей входной переменной, подобных этому. Это не самое лучшее решение, особенно если у вас более одной входной переменной.

   private _user: any;
  @Input()
  set user(value: any) {
    this._user = value;
    // run ngOnChanges stuff here
    this.ngOnChanges();
  }
  get user(): any {
    return this._user;
  }
  

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

1. Я как бы искал альтернативу получения / установки, но подумал, что это возможно только для отдельных реквизитов, даст этому шанс. Я думаю, что в вашем текущем коде есть некоторые опечатки в скобках у пользователя getter , просто предупреждаю!

2. @JordanLewallen раздражает, что в их документации onChanges не приводится пример с объектом, а приводится только один для примитива. Возможно, эта проблема была исправлена в более поздних версиях, но мой проект Angular 6.0.1 по-прежнему не работает, когда я проверял последний раз.

3. поэтому я попытался «очистить» user переменную непосредственно перед ее назначением в моем parseUser() методе, выполнив: this.user = null чуть выше this.user = user , но даже это, похоже, не помогает при использовании get / set .

4. Я вернулся и снова посмотрел на свой код, где я делал это, и, похоже, я решил это по-другому, чем я думал. Поскольку у меня была служба, организующая изменения, я добавил наблюдаемый, на который я мог подписаться, который срабатывает в любое время, когда у меня были изменения. Извините, что потратил ваше время на это. Надеюсь, мой другой ответ сработает для вас. Приветствия.