DOM компонента не обновляется, если данные изменяются при просмотре другого компонента

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

Проблема в том, что если я в настоящее время просматриваю компонент B, когда данные в компоненте A изменяются, DOM не обновляется новыми данными, когда я возвращаюсь к компоненту A. (список ngFor будет продолжать отображать test1, test2 вместо данных получено.) Однако, используя console.info, я вижу в Dev Tools, что данные все еще принимаются компонентом A, когда я просматриваю компонент B. Если я нахожусь в компоненте A, когда новые данные получены, DOM обновляется и новые данные отображаются правильно.

Есть ли что-то, что мне нужно сделать, чтобы убедиться, что DOM обновляется при переходе обратно к компоненту? Я полагаю, что, вероятно, мог бы обнаружить NavigationEnd и присвоить данные самому себе. Есть ли что-то, что мне не хватает?

Заранее спасибо, вот код:

Компонент А

import { Component, OnInit, ChangeDetectionStrategy, NgZone } from '@angular/core';
import { DataService } from '../../services/data.service';

@Component({
  selector: 'app-metars',
  templateUrl: './metars.component.html',
  styleUrls: ['./metars.component.scss'],
  changeDetection: ChangeDetectionStrategy.Default
})
export class TestComponent implements OnInit {

  public dataArray = ['test1', 'test2'];

  constructor(private _dataService: DataService, private zone: NgZone) { }

  ngOnInit() {
    this._dataService.data$
    .subscribe( data => {
        this.zone.run(() => {

          this.dataArray = data;
          console.info(data); // When in Component B, this is still firing in Dev Tools and I can see the new data being pushed.
        });
      }
    );
  }

}

Компонент А HTML

<ul *ngFor = "let data of dataArray">
    {{ data }}
</ul>
<br/>

Служба данных

export class DataService {

  private data: string[] = new Array<string>();
  private _dataSource = new Subject<string[]>();

  data$ = this._dataSource.asObservable();


  constructor(public electronService: ElectronService) {

    this.intitalizeIPCs();
  }


  intitalizeIPCs() {

    this.electronService.ipcRenderer.on('receive-data', (event, arg) => {
      console.info('received data');
      this.data = arg;
      this._dataSource.next(this.data);
    });
  }

App.component.html

<app-nav></app-nav>
<div class = "content">
    <router-outlet></router-outlet>
</div>

РЕДАКТИРОВАТЬ - РЕШЕНИЕ С КОДОМ

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

Услуга:

import { Injectable, NgZone } from '@angular/core';
import { ElectronService } from '../providers/electron.service';
import { Observable, BehaviorSubject } from 'rxjs';

@Injectable({
  providedIn: 'root'
})

export class DataService {

  private _data: BehaviorSubject<Array<string>> = new BehaviorSubject([]);
  public readonly data: Observable<Array<string>> = this._data.asObservable(); // Access this directly using async pipe.

  constructor(public electronService: ElectronService, private zone: NgZone) {

    this.intitalizeIPCs();
  }

  intitalizeIPCs() {
    this.electronService.ipcRenderer.on('receive-data', (event, arg) => {
      this.zone.run(() => {
        this._data.next( arg );
      });
    });
  }
}

Компонент:

import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core';
import { DataService } from '../../services/metar.service';

@Component({
  selector: 'app-example',
  templateUrl: './example.component.html',
  styleUrls: ['./example.component.scss'],
  changeDetection: ChangeDetectionStrategy.Default
})
export class ExampleComponent implements OnInit {

  constructor(private _dataService: DataService) { }

  ngOnInit() {
  }


}

HTML

<ul *ngFor = "let metar of _metarService.metars | async">
    {{ metar }}
</ul>
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Angular и React для вашего проекта веб-разработки?
Angular и React для вашего проекта веб-разработки?
Когда дело доходит до веб-разработки, выбор правильного front-end фреймворка имеет решающее значение. Angular и React - два самых популярных...
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Мы провели Twitter Space, обсудив несколько проблем, связанных с последними дополнениями в Angular. Также прошла Angular Tiny Conf с 25 докладами.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
Мое недавнее углубление в Angular
Мое недавнее углубление в Angular
Недавно я провел некоторое время, изучая фреймворк Angular, и я хотел поделиться своим опытом со всеми вами. Как человек, который любит глубоко...
Освоение Observables и Subjects в Rxjs:
Освоение Observables и Subjects в Rxjs:
Давайте начнем с основ и постепенно перейдем к более продвинутым концепциям в RxJS в Angular
0
0
109
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я настоятельно рекомендую использовать канал async вместо подписки на поток. То, что вы испытываете, — это не неисправный компонент, а утечка памяти.

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

Проблема в том, что изменение маршрута к компоненту B позволяет Angular отказаться от экземпляра компонента A. Таким образом, Angular больше не связан с ним. С его точки зрения компонент больше не существует. Когда вы переключитесь обратно на компонент A, Angular создаст новый экземпляр, и он начнется с ['test1', 'test2'].

Я собираюсь взглянуть на асинхронный канал, спасибо за это. Тем не менее, если я помещу console.info в ngOnInit для компонента A и вернусь к нему, я не увижу, что он появился. Вы уверены, что создается новый экземпляр, или я что-то упустил?

Kezz 09.02.2019 18:31

@Kezz Если компоненты управляются маршрутизатором, то создается новый экземпляр, да. Может быть, вы опубликуете сообщение о конфигурации маршрутизатора, чтобы быть уверенным.

a better oliver 09.02.2019 19:00

Очевидно, что это нечто большее, чем то, что я читал, но я не могу понять, как заставить это работать. Мой список остается пустым, когда data$ изменяется в DataService. Разве я не могу просто сделать это в компоненте A: dataArray$: Observable<string[]> = this._dataService.data$ и <ul *ngFor = "let data of dataArray$ | async"> {{ data}} </ul>

Kezz 09.02.2019 21:36

@Кезз Абсолютно. Но, как уже упоминалось, это также вопрос времени. Если компонент создается при каждом изменении маршрута, предыдущие значения теряются. Вы можете обойти это, заменив Subject() на ReplaySubject(1).

a better oliver 10.02.2019 15:23

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

Kezz 12.02.2019 01:40

Другие вопросы по теме