订阅可观察但运行时未准备好的数据



我在将服务中可观察量中的数据与使用数据的组件同步时遇到问题。数据服务在创建 api 服务时调用它,以生成从服务器获取的设备列表。这似乎工作正常,因为设备列表是在数据服务中构建的。

import { Injectable } from '@angular/core';
import { ApiService } from './api.service';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class DeviceManagerService {
devicesInfo = null;
deviceInfo = null;
devices = [];
constructor(private apiService: ApiService ) { 
this.apiService.getDeviceStatus().subscribe((resp) => {
this.devicesInfo = resp;
console.log('DeviceManager: deviceInfo: ',this.devicesInfo);
this.buildDeviceTable(resp);
console.log('devices[]=', this.devices);
}); 
}
buildDeviceTable(devicesInfo) {
devicesInfo.record.forEach( device => {
console.log('record.devid= ', device.devid);
if ( this.devices.indexOf(device.devid) > -1) {
//console.log('element ', device.devid, ' already in devices array');
}
else {
//this.devices.push({ device: device.devid });
this.devices.push(device.devid);
//console.log('added ', device.devid, ' to devices array');
}
})
}
getDevices(): Observable<string[]> {
let data = new Observable<string[]>(observer => {
observer.next(this.devices);
});
return data;
}
}

在我的组件中,我想在 mat-table 中显示该设备列表。我在数据服务中将设备列表设置为可观察量,并且组件订阅了该列表。但是当订阅函数运行时,数据不会在可观察量中返回 - 数组为空,forEach 循环不会运行以将设备 ID 从可观察量复制到用作 mat-table 数据源的本地数组中。

import { DeviceManagerService } from './../../services/device-manager.service';
import { Component, OnInit } from '@angular/core';
import { MatTableModule, MatTableDataSource } from '@angular/material';
import { Observable } from 'rxjs';
@Component({
selector: 'app-devices',
templateUrl: './devices.component.html',
styleUrls: ['./devices.component.css']
})
export class DevicesComponent implements OnInit {
displayedColumns: string[] = ['deviceID'];
//devices = null;
devices=["test1","test2"];
deviceData = null;
constructor(private deviceManager: DeviceManagerService) {
this.deviceManager.getDevices().subscribe( devTable => {
console.log('devTable: length', devTable.length);
devTable.forEach( device => {
console.log('forEach: device: ', device);
});
console.log('devices: ', this.devices);
//this.devices = devTable;
});   
}
ngOnInit() {
this.deviceData = new MatTableDataSource<any>(this.devices);
console.log('devicessComponent: ', this.deviceData); 
}
}

设备保持其默认初始化值,并且永远不会分配来自设备管理器的值。但是设备管理器已经构建了正确的设备列表,由于某种原因,它没有通过订阅功能进入组件。

如何确保在订阅函数中提供数据?

谢谢。。。。

您应该使用ReplaySubjectBehaviorSubject。如果您只希望它在发出第一个值时发出一个值,则将使用第一个值。后者,如果您希望它具有默认值,例如,空数组。

您甚至可以将服务更新为完全使用流,这将使您的数据始终保持最新状态,并大大减少.subscribe的使用,这是内存泄漏(未关闭订阅(的主要原因:

@Injectable({
providedIn: 'root'
})
export class DeviceManagerService {
readonly devicesInfo$ = this.apiService.getDeviceStatus().pipe(
shareReplay(1)
);
readonly devices$ = this.devicesInfo$.pipe(
map((devicesInfo) => devicesInfo.record
.filter((dev, i, arr) => arr.findIndex(({ devid }) => devid === dev.devid) === i)
),
shareReplay(1)
);
constructor(private apiService: ApiService ) {}
}

而且由于您现在使用的是流,因此MatTableDataSource的使用已经过时了,因为您可以将可观察量作为数据源传递给 mat-table:

export class DevicesComponent {
displayedColumns: string[] = ['deviceID'];
readonly devices$ = this.deviceManager.devices$;
constructor(private deviceManager: DeviceManagerService) {}
}

如您所见,我不使用BehaviorSubjectReplaySubject,但我确实使用shareReplay()运算符。这基本上在ReplaySubject中变成了可观察对象,并在订阅者之间共享订阅。

但请注意,应谨慎使用shareReplay(1)。如果可观察量未完成,则订阅不会结束,即使组件被销毁也是如此。您可以添加takeUntil(//destroy observable),也可以将其更改为shareReplay({ refCount: true, bufferSize: 1 })。后者将在订阅计数达到 0 后重新启动原始Observable,并在之后再次订阅。不过,好消息是,事情真的被清理干净了。所以。。这只是一个脚注:)

我不是 100% 确定,但我相信它不起作用的原因是在将数据传递给它之前需要订阅您的可观察量。在你的代码中,你首先用observer.next(this.devices(传递数据,然后订阅。在您订阅时,数据已经发送,事件已经完成。

有几种方法可以解决这个问题,一种是使用BehaviorSubject。

  1. 在服务的顶部,添加属性devices$ = new BehaviourSubject<string[]>([]);

  2. 在方法"buildDeviceTable(("的末尾,您添加行this.devices$.next(this.devices);

  3. 在组件中,将this.deviceManager.getDevices().subscribe(替换为this.deviceManager.devices$.subscribe(

最新更新