如何使用 BehaviourSubjects 在 Angular 中的组件之间共享来自 API 调用的数据?
How to use BehaviourSubjects to share data from API call between components in Angular?
我目前正在构建一个 Angular 应用程序,我在其中向 api 发出请求,并将响应映射到两个不同的数组。我可以在我的 app.components.ts
中使用这些数据,但我会根据需要制作新组件。如何在组件之间共享数据以确保组件始终具有最新数据,因为我还需要定期调用 API.
我在 SO 和一些 youtube 视频上看到了一些答案,但我并不完全理解它。
我的服务代码是
url = 'https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_day.geojson';
private _earthquakePropertiesSource = new BehaviorSubject<Array<any>>([]);
private _earthquakeGeometrySource = new BehaviorSubject<Array<any>>([]);
constructor(private readonly httpClient: HttpClient) {
}
public getEarthquakeData(): Observable<{ properties: [], geometries: []}> {
return this.httpClient.get<any>(this.url).pipe(
// this will run when the response comes back
map((response: any) => {
return {
properties: response.features.map(x => x.properties),
geometries: response.features.map(x => x.geometry)
};
})
);
}
它在我的 app.component.ts
中使用如下:
properties: Array<any>;
geometries: Array<any>;
constructor(private readonly earthquakeService: EarthquakeService) {
}
ngOnInit() {
this.earthquakeService.getEarthquakeData().subscribe(data => {
this.properties = data.properties;
this.geometries = data.geometries;
this.generateMapData();
});
}
generateMapData() {
for (const g of this.geometries) {
const tempData: any = {
latitude: g.coordinates[0],
longitude: g.coordinates[1],
draggable: false,
};
this.mapData.push(tempData);
}
如有任何帮助,我们将不胜感激。
编辑 1
服务:
url = 'https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_day.geojson';
properties = new BehaviorSubject<Array<any>>([]);
geometries = new BehaviorSubject<Array<any>>([]);
constructor(private readonly httpClient: HttpClient) {
loadEarthquakeData().
}
public loadEarthquakeData(): Observable<{ properties: [], geometries: []}> {
return this.httpClient.get<any>(this.url).pipe(
tap((response: any) => {
this.properties.next(response.features.map(x => x.properties);
this.geometries.next(response.features.map(x => x.geometry));
})
).toPromise();
}
组件:
private _subscription: Subscription;
constructor(private readonly earthquakeService: EarthquakeService) {
}
ngOnInit() {
this.generateMapData();
}
ngOnDestroy() {
if (this._subscription) {
this._subscription.unsubscribe();
}
}
generateMapData() {
this._subscription = this.earthquakeService.geometries.subscribe(geometries => {
for (const g of this.earthquakeService.geometries.getValue()) {
const tempData: any = {
latitude: g.coordinates[0],
longitude: g.coordinates[1],
draggable: false,
};
this.mapData.push(tempData);
}
});
}
原创
为此,您需要 Angular Services
它们是可以像共享状态一样运行的单例。您要做的是将您的数据存储在服务中,然后从您的两个组件调用服务并收听服务的 BehaviorSubject。
要在组件之间共享信息,您可以在将在不同组件中使用的服务中使用 behaviorSubject。
BehaviorSubject 的特点是它存储“当前”值,即最后一个值,需要与其他组件共享。
它的特殊性在于:
需要一个初始值
const subject = new MyBehaviorSubject('initialValue');
return主题最后值
您可以使用 getValue() 方法检索最后一个值(不可观察)
subject.getValue()
可以订阅了:
subject.subscribe(console.log);
用 next()
更新值
subject.next('New value');
我举个例子:
在我的服务中:
private isOpen = new BehaviorSubject<boolean>(false);
public getNavBarOpen(): Observable<boolean> {
return this.isOpen.asObservable();
}
setNavBarOpen(status: boolean): void {
this.isOpen.next(status);
}
在我的组件中:
如果我想更新值:
this.myService.setNavBarOpen(true);
如果我想获取值:
this.myService.getNavBarOpen().subscribe()
您可以在将保存此数据的服务中包含一个 属性,然后订阅它。我假设您将有一个定时间隔检查新响应 - 然后可以更新服务中 属性 的值。
export interface earthQuakeResponse {
properties: Array<any>
geometries: Array<any>
}
export class EarthQuakeService {
private _earthQuakeResponse = new BehaviorSubject<earthQuakeResponse>([]);
readonly earthQuakeResponse = this._earthQuakeResponse.asObservable();
public getEarthquakeData(): Observable<earthQuakeResponse> {
return this.earthQuakeResponse;
}
//call this method when you want to update your data
private updateData() {
this.httpClient.get<any>(this.url).subscribe(
response => {
this._earthQuakeResponse.next({
properties: response.features.map(x => x.properties),
geometries: response.features.map(x => x.geometry)
});
});
}
}
这是一个描述如何使用纯 RxJS 完成的答案。另一种选择是使用 NgRx。
首先,你设置了两个科目。意图是所有组件都将订阅它们并在刷新时接收最新数据?
你应该使用 ReplaySubject
而不是 BehaviorSubject
,因为你没有任何初始状态。由于数据作为一件事返回,我将使用一个主题。
首先,我将声明一个接口,以便于讨论数据类型。
地震-data.ts
export interface EarthquakeData {
// TODO: create types for these
geometries: any[];
properties: any[];
}
在您的服务中,您可以通过自己的方法公开数据来分离检索和通知。
earthquake.service.ts
url = 'https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_day.geojson';
private _earthquakeData$ = new ReplaySubject<EarthquakeData>(1);
constructor(private readonly httpClient: HttpClient) {}
getEarthquakeData(): Observable<EarthquakeData> {
// return the subject here
// subscribers will will notified when the data is refreshed
return this._earthquakeData$.asObservable();
}
refreshEarthquakeData(): Observable<void> {
return this.httpClient.get<any>(this.url).pipe(
tap(response => {
// notify all subscribers of new data
this._earthquakeData$.next({
geometries: response.features.map(x => x.geometry),
properties: response.features.map(x => x.properties)
});
})
);
}
所以现在,所有想要接收数据的组件都将订阅一个方法:
private destroyed$ = new Subject();
ngOnInit()
this.earthquakeService.getEarthquakeData().pipe(
// it is now important to unsubscribe from the subject
takeUntil(this.destroyed$)
).subscribe(data => {
console.log(data); // the latest data
});
}
ngOnDestroy() {
this.destroyed$.next();
this.destroyed$.complete();
}
并且您可以在任何地方刷新数据:
refreshData() {
this.refreshing = true;
this.earthquakeService.refreshEarthquakeData().subscribe(() => {
this.refreshing = false;
});
}
解决此问题的简单方法是使用 BehaviorSubject
。这方面的文档很全面,相信你能找到。
为了处理大型应用程序中的复杂状态,人们使用 Redux。对于Angular,有NgRx。
如果更新状态需要调用 API 作为副作用,请使用 ngrx/effects
服务方法不需要return Observable:
public getEarthquakeData(): Observable<{ properties: [], geometries: []}> {
return this.httpClient.get<any>(this.url).pipe(
// this will run when the response comes back
tap((response: any) => {
_earthquakePropertiesSource.next(response.features.map(x => x.properties));
_earthquakeGeometrySource.next(response.features.map(x => x.geometry));
})
});
和组件:
ngOnInit() {
combineLatest(
this.earthquakeService._earthquakePropertiesSource,
this.earthquakeService._earthquakeGeometrySource
).subscribe(data => {
this.properties = data[0];
this.geometries = data[1];
this.generateMapData();
});
}
我目前正在构建一个 Angular 应用程序,我在其中向 api 发出请求,并将响应映射到两个不同的数组。我可以在我的 app.components.ts
中使用这些数据,但我会根据需要制作新组件。如何在组件之间共享数据以确保组件始终具有最新数据,因为我还需要定期调用 API.
我在 SO 和一些 youtube 视频上看到了一些答案,但我并不完全理解它。
我的服务代码是
url = 'https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_day.geojson';
private _earthquakePropertiesSource = new BehaviorSubject<Array<any>>([]);
private _earthquakeGeometrySource = new BehaviorSubject<Array<any>>([]);
constructor(private readonly httpClient: HttpClient) {
}
public getEarthquakeData(): Observable<{ properties: [], geometries: []}> {
return this.httpClient.get<any>(this.url).pipe(
// this will run when the response comes back
map((response: any) => {
return {
properties: response.features.map(x => x.properties),
geometries: response.features.map(x => x.geometry)
};
})
);
}
它在我的 app.component.ts
中使用如下:
properties: Array<any>;
geometries: Array<any>;
constructor(private readonly earthquakeService: EarthquakeService) {
}
ngOnInit() {
this.earthquakeService.getEarthquakeData().subscribe(data => {
this.properties = data.properties;
this.geometries = data.geometries;
this.generateMapData();
});
}
generateMapData() {
for (const g of this.geometries) {
const tempData: any = {
latitude: g.coordinates[0],
longitude: g.coordinates[1],
draggable: false,
};
this.mapData.push(tempData);
}
如有任何帮助,我们将不胜感激。
编辑 1
服务:
url = 'https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_day.geojson';
properties = new BehaviorSubject<Array<any>>([]);
geometries = new BehaviorSubject<Array<any>>([]);
constructor(private readonly httpClient: HttpClient) {
loadEarthquakeData().
}
public loadEarthquakeData(): Observable<{ properties: [], geometries: []}> {
return this.httpClient.get<any>(this.url).pipe(
tap((response: any) => {
this.properties.next(response.features.map(x => x.properties);
this.geometries.next(response.features.map(x => x.geometry));
})
).toPromise();
}
组件:
private _subscription: Subscription;
constructor(private readonly earthquakeService: EarthquakeService) {
}
ngOnInit() {
this.generateMapData();
}
ngOnDestroy() {
if (this._subscription) {
this._subscription.unsubscribe();
}
}
generateMapData() {
this._subscription = this.earthquakeService.geometries.subscribe(geometries => {
for (const g of this.earthquakeService.geometries.getValue()) {
const tempData: any = {
latitude: g.coordinates[0],
longitude: g.coordinates[1],
draggable: false,
};
this.mapData.push(tempData);
}
});
}
原创
为此,您需要 Angular Services
它们是可以像共享状态一样运行的单例。您要做的是将您的数据存储在服务中,然后从您的两个组件调用服务并收听服务的 BehaviorSubject。
要在组件之间共享信息,您可以在将在不同组件中使用的服务中使用 behaviorSubject。
BehaviorSubject 的特点是它存储“当前”值,即最后一个值,需要与其他组件共享。
它的特殊性在于:
需要一个初始值
const subject = new MyBehaviorSubject('initialValue');
return主题最后值
您可以使用 getValue() 方法检索最后一个值(不可观察)
subject.getValue()
可以订阅了:
subject.subscribe(console.log);
用 next()
更新值subject.next('New value');
我举个例子: 在我的服务中:
private isOpen = new BehaviorSubject<boolean>(false);
public getNavBarOpen(): Observable<boolean> {
return this.isOpen.asObservable();
}
setNavBarOpen(status: boolean): void {
this.isOpen.next(status);
}
在我的组件中:
如果我想更新值:
this.myService.setNavBarOpen(true);
如果我想获取值:
this.myService.getNavBarOpen().subscribe()
您可以在将保存此数据的服务中包含一个 属性,然后订阅它。我假设您将有一个定时间隔检查新响应 - 然后可以更新服务中 属性 的值。
export interface earthQuakeResponse {
properties: Array<any>
geometries: Array<any>
}
export class EarthQuakeService {
private _earthQuakeResponse = new BehaviorSubject<earthQuakeResponse>([]);
readonly earthQuakeResponse = this._earthQuakeResponse.asObservable();
public getEarthquakeData(): Observable<earthQuakeResponse> {
return this.earthQuakeResponse;
}
//call this method when you want to update your data
private updateData() {
this.httpClient.get<any>(this.url).subscribe(
response => {
this._earthQuakeResponse.next({
properties: response.features.map(x => x.properties),
geometries: response.features.map(x => x.geometry)
});
});
}
}
这是一个描述如何使用纯 RxJS 完成的答案。另一种选择是使用 NgRx。
首先,你设置了两个科目。意图是所有组件都将订阅它们并在刷新时接收最新数据?
你应该使用 ReplaySubject
而不是 BehaviorSubject
,因为你没有任何初始状态。由于数据作为一件事返回,我将使用一个主题。
首先,我将声明一个接口,以便于讨论数据类型。
地震-data.ts
export interface EarthquakeData {
// TODO: create types for these
geometries: any[];
properties: any[];
}
在您的服务中,您可以通过自己的方法公开数据来分离检索和通知。
earthquake.service.ts
url = 'https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_day.geojson';
private _earthquakeData$ = new ReplaySubject<EarthquakeData>(1);
constructor(private readonly httpClient: HttpClient) {}
getEarthquakeData(): Observable<EarthquakeData> {
// return the subject here
// subscribers will will notified when the data is refreshed
return this._earthquakeData$.asObservable();
}
refreshEarthquakeData(): Observable<void> {
return this.httpClient.get<any>(this.url).pipe(
tap(response => {
// notify all subscribers of new data
this._earthquakeData$.next({
geometries: response.features.map(x => x.geometry),
properties: response.features.map(x => x.properties)
});
})
);
}
所以现在,所有想要接收数据的组件都将订阅一个方法:
private destroyed$ = new Subject();
ngOnInit()
this.earthquakeService.getEarthquakeData().pipe(
// it is now important to unsubscribe from the subject
takeUntil(this.destroyed$)
).subscribe(data => {
console.log(data); // the latest data
});
}
ngOnDestroy() {
this.destroyed$.next();
this.destroyed$.complete();
}
并且您可以在任何地方刷新数据:
refreshData() {
this.refreshing = true;
this.earthquakeService.refreshEarthquakeData().subscribe(() => {
this.refreshing = false;
});
}
解决此问题的简单方法是使用 BehaviorSubject
。这方面的文档很全面,相信你能找到。
为了处理大型应用程序中的复杂状态,人们使用 Redux。对于Angular,有NgRx。
如果更新状态需要调用 API 作为副作用,请使用 ngrx/effects
服务方法不需要return Observable:
public getEarthquakeData(): Observable<{ properties: [], geometries: []}> {
return this.httpClient.get<any>(this.url).pipe(
// this will run when the response comes back
tap((response: any) => {
_earthquakePropertiesSource.next(response.features.map(x => x.properties));
_earthquakeGeometrySource.next(response.features.map(x => x.geometry));
})
});
和组件:
ngOnInit() {
combineLatest(
this.earthquakeService._earthquakePropertiesSource,
this.earthquakeService._earthquakeGeometrySource
).subscribe(data => {
this.properties = data[0];
this.geometries = data[1];
this.generateMapData();
});
}