行为主题只显示初始值/重播主题不显示更新值
Behaviour subject only shows initial value / Replay Subject does not show updated Value
背景:
我有一项服务,其中有一个基于开放图层的地图。地图组件有自己的服务。
单击图标时会生成一个弹出窗口。在这个弹出窗口中有一个按钮,单击该按钮会从弹出窗口中显示的对象中获取一些数据,然后导航到不同的组件。
我正在尝试将此数据 publish/add 发送到行为主题,然后在新组件中订阅它。单击图标时,数据将添加到行为主题。弹出组件有自己的服务。
在目标组件中,我尝试使用异步管道订阅模板中的弹出服务行为主题。
问题:
行为主体上的可观察值只有returns初始值。行为主题的下一个功能似乎没有向其添加新数据。它总是给出新的值。
使用回放主题时,没有任何显示。下一个功能似乎根本没有效果。
代码:
弹出式服务
_analysisBehaviourSubject: BehaviorSubject<any> = new BehaviorSubject();
// other code
addElement(element: any) {
console.log('adding new element to behaviour subject', element);
this._analysisBehaviourSubject.next(element);
}
getElement$(): Observable<any> {
return this._analysisBehaviourSubject.asObservable();
}
地图服务
getAnalysisBehaviourSubject(): Observable<any> {
return this.clusPopupService.getElement$();
}
目标服务
getAnalysisBehaviourSubject(): Observable<any> {
return this.mapService.getAnalysisBehaviourSubject();
}
然后在目标组件中只有一个异步管道。
我已经尝试过的:
直接从弹出服务导入行为主题,而不是通过我的项目结构移动它。
使用行为主题而不是示例代码中所示的重播主题。
将所有三个服务添加到 app.module.ts 中的提供程序数组。行为主体仍然发出原始值,而重放主体什么也没有。
更新:
弹出式服务
import { DataPreparationService } from './data-preparation.service';
import { GeneralService } from '../../general.service';
import { Injectable } from '@angular/core';
import OlMap from 'ol/Map';
import OlView from 'ol/View';
import { fromLonLat } from 'ol/proj';
import Overlay from 'ol/Overlay';
import Point from 'ol/geom/Point.js';
import { SourceDataObject } from '../../Models/sourceDataObj';
import { Select } from 'ol/interaction';
import { CookieService } from 'ngx-cookie-service';
import { StoreService } from 'src/app/store.service';
@Injectable({
providedIn: 'root'
})
export class ClusterPopupService {
// analysis variables
deltaEX = 1;
deltaEy = 3;
overlayCoords: object = {};
sourceDataObj: SourceDataObject = { element: '', coordinates: null };
detectedPoints: Point[] = [];
// pop up variables
foundIcon: SourceDataObject;
newPoint: Point;
generalSelect: Select;
num = 0;
// analysis page Behaviour Subjects
constructor(
private genService: GeneralService,
private dataPrepService: DataPreparationService,
private cookies: CookieService,
private store: StoreService
) {}
// behaviour subject m1ethods
//cluster methods
filterPoints(newPoint: Point) {
const newPointCoords: number[] = newPoint.getCoordinates();
if (this.detectedPoints.length >= 1) {
for (let i = 0; i <= this.detectedPoints.length - 1; i++) {
const savedPointCoords = this.detectedPoints[i].getCoordinates();
if (
savedPointCoords[0] === newPointCoords[0] &&
savedPointCoords[1] === newPointCoords[1]
) {
break;
} else if (i === this.detectedPoints.length - 1) {
this.detectedPoints.push(newPoint);
}
}
} else {
this.detectedPoints.push(newPoint);
}
}
async clearFeatureCache() {
this.foundIcon = undefined;
this.generalSelect.getFeatures().clear();
this.detectedPoints = null;
}
pushToCookies(currView: OlView) {
this.cookies.deleteAll();
const currCoordinates: number[] = currView.getCenter();
const longitudeString: string = currCoordinates[0].toString();
const latitudeString: string = currCoordinates[1].toString();
const zoomLevelString: string = currView.getZoom().toString();
this.cookies.set('longitude', longitudeString, 1 / 48);
this.cookies.set('latitude', latitudeString, 1 / 48);
this.cookies.set('zoom', zoomLevelString, 1 / 48);
console.log(
this.cookies.get('longitude'),
this.cookies.get('latitude'),
this.cookies.get('zoom')
);
}
async preparePopup(
fishCoords: SourceDataObject[],
munitCoords: SourceDataObject[],
wrackCoords: SourceDataObject[],
sedimentCoords: SourceDataObject[],
generalSelect: Select,
view: OlView,
globalMap: OlMap,
localDoc?: Document
) {
const extent: number[] = view.calculateExtent(globalMap.getSize());
const container = localDoc.getElementById('popup');
const content = localDoc.getElementById('popup-content');
const closer = localDoc.getElementById('popup-closer');
const analyseButton = localDoc.getElementById('analyseButton');
const popUpOverlay: Overlay = new Overlay({
element: container,
autoPan: true
});
this.generalSelect = generalSelect;
closer.onclick = () => {
popUpOverlay.setPosition(undefined);
this.foundIcon = undefined;
generalSelect.getFeatures().clear();
this.detectedPoints = null;
this.newPoint = null;
closer.blur();
return false;
};
globalMap.addOverlay(popUpOverlay);
generalSelect.on('select', event => {
this.newPoint = event.selected[0].getGeometry() as Point;
this.foundIcon = this.dataPrepService.binSearch(
wrackCoords,
this.newPoint.getCoordinates(),
'wrack Array'
);
if (this.foundIcon === undefined) {
this.foundIcon = this.dataPrepService.binSearch(
sedimentCoords,
this.newPoint.getCoordinates(),
'sediment array'
);
}
if (this.foundIcon === undefined) {
this.foundIcon = this.dataPrepService.binSearch(
fishCoords,
this.newPoint.getCoordinates(),
'fish array'
);
}
if (this.foundIcon === undefined) {
this.foundIcon = this.dataPrepService.binSearch(
munitCoords,
this.newPoint.getCoordinates(),
'munition array'
);
}
if (this.foundIcon !== undefined) {
this.sourceDataObj = this.foundIcon;
popUpOverlay.setPosition(fromLonLat(this.foundIcon.coordinates));
} else {
if (this.sourceDataObj === null) {
this.sourceDataObj = { element: '', coordinates: null };
this.sourceDataObj.element = 'not found';
console.log(this.genService.backEndUri);
}
this.sourceDataObj.element = 'not found';
popUpOverlay.setPosition(this.newPoint.getCoordinates());
}
console.log('the icon found is:', this.foundIcon);
switch (this.sourceDataObj.element) {
case 'sediment':
content.innerHTML =
'<p>You clicked on a <code>' +
this.sourceDataObj.element +
'</code> with coordinates <code>' +
this.sourceDataObj.coordinates +
'</code>, and ID <code>' +
this.sourceDataObj.sampleId +
'</code>. Analyse this element?</p><p><button onclick="window.location.href=\'' +
this.genService.localUri +
'/analyseSediment' +
'\'"' +
'id="analyseButton">Analyse</button></p>';
this.sourceDataObj = null;
this.pushToCookies(view);
break;
case 'fish':
this.store.addElement(this.sourceDataObj.miscData);
content.innerHTML =
'<p>You clicked on a <code>' +
this.sourceDataObj.element +
'</code> with coordinates <code>' +
this.sourceDataObj.coordinates +
'</code>, cruise ID <code>' +
this.sourceDataObj.miscData.cruiseId +
'</code> and target ID <code>' +
this.sourceDataObj.miscData.sampleId +
'</code>. Analyse this element?</p><p><button onclick="window.location.href=\'' +
this.genService.localUri +
'/analyseFish' +
'\'"' +
' id="analyseButton">Analyse</button></p>';
this.sourceDataObj = null;
this.pushToCookies(view);
break;
case 'Munition':
content.innerHTML =
'<p>You clicked on a <code>' +
this.sourceDataObj.element +
'</code> with coordinates <code>' +
this.sourceDataObj.coordinates +
'</code>, cruise ID <code>' +
this.sourceDataObj.miscData.cruiseId +
'</code> and target ID <code>' +
this.sourceDataObj.miscData.targetId +
'</code>. Analyse this element?</p><p><button onclick="window.location.href=\'' +
this.genService.localUri +
'/analyseMunition' +
'\'"' +
'id="analyseButton">Analyse</button></p>';
this.sourceDataObj = null;
this.pushToCookies(view);
break;
case 'wrack':
content.innerHTML =
'<p>You clicked on a <code>' +
this.sourceDataObj.element +
'</code> with coordinates <code>' +
this.sourceDataObj.coordinates +
'</code>, and target ID <code>' +
this.sourceDataObj.miscData.targetId +
'</code>. Analyse this element?</p><p><button onclick="window.location.href=\'' +
this.genService.localUri +
'/analyseWrack' +
'\'"' +
' id="analyseButton">Analyse</button></p>';
this.sourceDataObj = null;
this.pushToCookies(view);
break;
case 'not found':
content.innerHTML =
'<p>You selected more than one Icon. Please Zoom in and select only one for analysis</p>';
break;
default:
content.innerHTML =
'<p>The map feature wasn"t quite selected. Please close the pop up and retry</p>';
break;
}
});
}
}
商店服务
import { Injectable } from '@angular/core';
import { BehaviorSubject, ReplaySubject } from 'rxjs';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class StoreService {
_analysisBehaviourSubject: BehaviorSubject<any> = new BehaviorSubject();
analysis$: Observable<any> = this._analysisBehaviourSubject.asObservable();
constructor() {
console.log('I AM THE STORE');
}
addElement(element: any) {
console.log('adding new element to behaviour subject', element);
this._analysisBehaviourSubject.next(element);
}
getElement$(): Observable<any> {
return this.analysis$;
}
}
我认为每次使用 asObservable() 函数调用 getElement$() 时都会创建一个新的可观察对象。我觉得 the docs 不是 100% 清楚这一点。
我的建议是在 class 级别而不是方法级别创建可观察对象。
因此,在您的 Popup 服务中,您可以这样做:
_analysisBehaviourSubject: ReplaySubject<any> = new ReplaySubject();
_analysis$: Observable<any> = this._analysisBehaviourSubject.asObservable();
或者,您可以直接订阅主题,因为它扩展了 Observable。
我明白了。主题图案及其阴影效果很好。
在我上面的弹出服务中,我通过 href 导航到该组件。我最好的猜测是这会破坏组件树,并且保存值的服务实例也会被破坏。
虽然我无法证明这一点,但我确实尝试过实现一个重播主题,以便在我从一个组件导航到另一个组件时进行更新,然后在我处于项目中的新组件时订阅它,并且效果很好美好的。所以我把这个固定在 href 的使用上。
在打开图层的内部 html 中按下按钮后,需要找到另一种路由到我的组件的方法。
感谢所有抽出时间阅读或回复本文的人。
背景:
我有一项服务,其中有一个基于开放图层的地图。地图组件有自己的服务。
单击图标时会生成一个弹出窗口。在这个弹出窗口中有一个按钮,单击该按钮会从弹出窗口中显示的对象中获取一些数据,然后导航到不同的组件。
我正在尝试将此数据 publish/add 发送到行为主题,然后在新组件中订阅它。单击图标时,数据将添加到行为主题。弹出组件有自己的服务。
在目标组件中,我尝试使用异步管道订阅模板中的弹出服务行为主题。
问题:
行为主体上的可观察值只有returns初始值。行为主题的下一个功能似乎没有向其添加新数据。它总是给出新的值。
使用回放主题时,没有任何显示。下一个功能似乎根本没有效果。
代码:
弹出式服务
_analysisBehaviourSubject: BehaviorSubject<any> = new BehaviorSubject();
// other code
addElement(element: any) {
console.log('adding new element to behaviour subject', element);
this._analysisBehaviourSubject.next(element);
}
getElement$(): Observable<any> {
return this._analysisBehaviourSubject.asObservable();
}
地图服务
getAnalysisBehaviourSubject(): Observable<any> {
return this.clusPopupService.getElement$();
}
目标服务
getAnalysisBehaviourSubject(): Observable<any> {
return this.mapService.getAnalysisBehaviourSubject();
}
然后在目标组件中只有一个异步管道。
我已经尝试过的:
直接从弹出服务导入行为主题,而不是通过我的项目结构移动它。
使用行为主题而不是示例代码中所示的重播主题。
将所有三个服务添加到 app.module.ts 中的提供程序数组。行为主体仍然发出原始值,而重放主体什么也没有。
更新: 弹出式服务
import { DataPreparationService } from './data-preparation.service';
import { GeneralService } from '../../general.service';
import { Injectable } from '@angular/core';
import OlMap from 'ol/Map';
import OlView from 'ol/View';
import { fromLonLat } from 'ol/proj';
import Overlay from 'ol/Overlay';
import Point from 'ol/geom/Point.js';
import { SourceDataObject } from '../../Models/sourceDataObj';
import { Select } from 'ol/interaction';
import { CookieService } from 'ngx-cookie-service';
import { StoreService } from 'src/app/store.service';
@Injectable({
providedIn: 'root'
})
export class ClusterPopupService {
// analysis variables
deltaEX = 1;
deltaEy = 3;
overlayCoords: object = {};
sourceDataObj: SourceDataObject = { element: '', coordinates: null };
detectedPoints: Point[] = [];
// pop up variables
foundIcon: SourceDataObject;
newPoint: Point;
generalSelect: Select;
num = 0;
// analysis page Behaviour Subjects
constructor(
private genService: GeneralService,
private dataPrepService: DataPreparationService,
private cookies: CookieService,
private store: StoreService
) {}
// behaviour subject m1ethods
//cluster methods
filterPoints(newPoint: Point) {
const newPointCoords: number[] = newPoint.getCoordinates();
if (this.detectedPoints.length >= 1) {
for (let i = 0; i <= this.detectedPoints.length - 1; i++) {
const savedPointCoords = this.detectedPoints[i].getCoordinates();
if (
savedPointCoords[0] === newPointCoords[0] &&
savedPointCoords[1] === newPointCoords[1]
) {
break;
} else if (i === this.detectedPoints.length - 1) {
this.detectedPoints.push(newPoint);
}
}
} else {
this.detectedPoints.push(newPoint);
}
}
async clearFeatureCache() {
this.foundIcon = undefined;
this.generalSelect.getFeatures().clear();
this.detectedPoints = null;
}
pushToCookies(currView: OlView) {
this.cookies.deleteAll();
const currCoordinates: number[] = currView.getCenter();
const longitudeString: string = currCoordinates[0].toString();
const latitudeString: string = currCoordinates[1].toString();
const zoomLevelString: string = currView.getZoom().toString();
this.cookies.set('longitude', longitudeString, 1 / 48);
this.cookies.set('latitude', latitudeString, 1 / 48);
this.cookies.set('zoom', zoomLevelString, 1 / 48);
console.log(
this.cookies.get('longitude'),
this.cookies.get('latitude'),
this.cookies.get('zoom')
);
}
async preparePopup(
fishCoords: SourceDataObject[],
munitCoords: SourceDataObject[],
wrackCoords: SourceDataObject[],
sedimentCoords: SourceDataObject[],
generalSelect: Select,
view: OlView,
globalMap: OlMap,
localDoc?: Document
) {
const extent: number[] = view.calculateExtent(globalMap.getSize());
const container = localDoc.getElementById('popup');
const content = localDoc.getElementById('popup-content');
const closer = localDoc.getElementById('popup-closer');
const analyseButton = localDoc.getElementById('analyseButton');
const popUpOverlay: Overlay = new Overlay({
element: container,
autoPan: true
});
this.generalSelect = generalSelect;
closer.onclick = () => {
popUpOverlay.setPosition(undefined);
this.foundIcon = undefined;
generalSelect.getFeatures().clear();
this.detectedPoints = null;
this.newPoint = null;
closer.blur();
return false;
};
globalMap.addOverlay(popUpOverlay);
generalSelect.on('select', event => {
this.newPoint = event.selected[0].getGeometry() as Point;
this.foundIcon = this.dataPrepService.binSearch(
wrackCoords,
this.newPoint.getCoordinates(),
'wrack Array'
);
if (this.foundIcon === undefined) {
this.foundIcon = this.dataPrepService.binSearch(
sedimentCoords,
this.newPoint.getCoordinates(),
'sediment array'
);
}
if (this.foundIcon === undefined) {
this.foundIcon = this.dataPrepService.binSearch(
fishCoords,
this.newPoint.getCoordinates(),
'fish array'
);
}
if (this.foundIcon === undefined) {
this.foundIcon = this.dataPrepService.binSearch(
munitCoords,
this.newPoint.getCoordinates(),
'munition array'
);
}
if (this.foundIcon !== undefined) {
this.sourceDataObj = this.foundIcon;
popUpOverlay.setPosition(fromLonLat(this.foundIcon.coordinates));
} else {
if (this.sourceDataObj === null) {
this.sourceDataObj = { element: '', coordinates: null };
this.sourceDataObj.element = 'not found';
console.log(this.genService.backEndUri);
}
this.sourceDataObj.element = 'not found';
popUpOverlay.setPosition(this.newPoint.getCoordinates());
}
console.log('the icon found is:', this.foundIcon);
switch (this.sourceDataObj.element) {
case 'sediment':
content.innerHTML =
'<p>You clicked on a <code>' +
this.sourceDataObj.element +
'</code> with coordinates <code>' +
this.sourceDataObj.coordinates +
'</code>, and ID <code>' +
this.sourceDataObj.sampleId +
'</code>. Analyse this element?</p><p><button onclick="window.location.href=\'' +
this.genService.localUri +
'/analyseSediment' +
'\'"' +
'id="analyseButton">Analyse</button></p>';
this.sourceDataObj = null;
this.pushToCookies(view);
break;
case 'fish':
this.store.addElement(this.sourceDataObj.miscData);
content.innerHTML =
'<p>You clicked on a <code>' +
this.sourceDataObj.element +
'</code> with coordinates <code>' +
this.sourceDataObj.coordinates +
'</code>, cruise ID <code>' +
this.sourceDataObj.miscData.cruiseId +
'</code> and target ID <code>' +
this.sourceDataObj.miscData.sampleId +
'</code>. Analyse this element?</p><p><button onclick="window.location.href=\'' +
this.genService.localUri +
'/analyseFish' +
'\'"' +
' id="analyseButton">Analyse</button></p>';
this.sourceDataObj = null;
this.pushToCookies(view);
break;
case 'Munition':
content.innerHTML =
'<p>You clicked on a <code>' +
this.sourceDataObj.element +
'</code> with coordinates <code>' +
this.sourceDataObj.coordinates +
'</code>, cruise ID <code>' +
this.sourceDataObj.miscData.cruiseId +
'</code> and target ID <code>' +
this.sourceDataObj.miscData.targetId +
'</code>. Analyse this element?</p><p><button onclick="window.location.href=\'' +
this.genService.localUri +
'/analyseMunition' +
'\'"' +
'id="analyseButton">Analyse</button></p>';
this.sourceDataObj = null;
this.pushToCookies(view);
break;
case 'wrack':
content.innerHTML =
'<p>You clicked on a <code>' +
this.sourceDataObj.element +
'</code> with coordinates <code>' +
this.sourceDataObj.coordinates +
'</code>, and target ID <code>' +
this.sourceDataObj.miscData.targetId +
'</code>. Analyse this element?</p><p><button onclick="window.location.href=\'' +
this.genService.localUri +
'/analyseWrack' +
'\'"' +
' id="analyseButton">Analyse</button></p>';
this.sourceDataObj = null;
this.pushToCookies(view);
break;
case 'not found':
content.innerHTML =
'<p>You selected more than one Icon. Please Zoom in and select only one for analysis</p>';
break;
default:
content.innerHTML =
'<p>The map feature wasn"t quite selected. Please close the pop up and retry</p>';
break;
}
});
}
}
商店服务
import { Injectable } from '@angular/core';
import { BehaviorSubject, ReplaySubject } from 'rxjs';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class StoreService {
_analysisBehaviourSubject: BehaviorSubject<any> = new BehaviorSubject();
analysis$: Observable<any> = this._analysisBehaviourSubject.asObservable();
constructor() {
console.log('I AM THE STORE');
}
addElement(element: any) {
console.log('adding new element to behaviour subject', element);
this._analysisBehaviourSubject.next(element);
}
getElement$(): Observable<any> {
return this.analysis$;
}
}
我认为每次使用 asObservable() 函数调用 getElement$() 时都会创建一个新的可观察对象。我觉得 the docs 不是 100% 清楚这一点。
我的建议是在 class 级别而不是方法级别创建可观察对象。
因此,在您的 Popup 服务中,您可以这样做:
_analysisBehaviourSubject: ReplaySubject<any> = new ReplaySubject();
_analysis$: Observable<any> = this._analysisBehaviourSubject.asObservable();
或者,您可以直接订阅主题,因为它扩展了 Observable。
我明白了。主题图案及其阴影效果很好。
在我上面的弹出服务中,我通过 href 导航到该组件。我最好的猜测是这会破坏组件树,并且保存值的服务实例也会被破坏。
虽然我无法证明这一点,但我确实尝试过实现一个重播主题,以便在我从一个组件导航到另一个组件时进行更新,然后在我处于项目中的新组件时订阅它,并且效果很好美好的。所以我把这个固定在 href 的使用上。
在打开图层的内部 html 中按下按钮后,需要找到另一种路由到我的组件的方法。
感谢所有抽出时间阅读或回复本文的人。