У меня есть приложение с несколькими компонентами, каждый из которых асинхронно отображает разные данные. Я использую 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>





Я настоятельно рекомендую использовать канал async вместо подписки на поток. То, что вы испытываете, — это не неисправный компонент, а утечка памяти.
Когда вы подписываетесь на data$, эта подписка будет оставаться в памяти до тех пор, пока подписка не будет отменена, чего в вашем случае никогда не происходит. Таким образом, экземпляр компонента A также должен оставаться в памяти.
Проблема в том, что изменение маршрута к компоненту B позволяет Angular отказаться от экземпляра компонента A. Таким образом, Angular больше не связан с ним. С его точки зрения компонент больше не существует. Когда вы переключитесь обратно на компонент A, Angular создаст новый экземпляр, и он начнется с ['test1', 'test2'].
@Kezz Если компоненты управляются маршрутизатором, то создается новый экземпляр, да. Может быть, вы опубликуете сообщение о конфигурации маршрутизатора, чтобы быть уверенным.
Очевидно, что это нечто большее, чем то, что я читал, но я не могу понять, как заставить это работать. Мой список остается пустым, когда data$ изменяется в DataService. Разве я не могу просто сделать это в компоненте A: dataArray$: Observable<string[]> = this._dataService.data$ и <ul *ngFor = "let data of dataArray$ | async"> {{ data}} </ul>
@Кезз Абсолютно. Но, как уже упоминалось, это также вопрос времени. Если компонент создается при каждом изменении маршрута, предыдущие значения теряются. Вы можете обойти это, заменив Subject() на ReplaySubject(1).
В очередной раз благодарим за помощь. Я отредактировал свой вопрос с новым кодом, использующим асинхронный канал. Дайте мне знать, если вы обнаружите какие-либо проблемы. Принял ваш ответ.
Я собираюсь взглянуть на асинхронный канал, спасибо за это. Тем не менее, если я помещу console.info в ngOnInit для компонента A и вернусь к нему, я не увижу, что он появился. Вы уверены, что создается новый экземпляр, или я что-то упустил?