行为主题只显示初始值/重播主题不显示更新值

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();
}

然后在目标组件中只有一个异步管道。

我已经尝试过的:

  1. 直接从弹出服务导入行为主题,而不是通过我的项目结构移动它。

  2. 使用行为主题而不是示例代码中所示的重播主题。

  3. 将所有三个服务添加到 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 中按下按钮后,需要找到另一种路由到我的组件的方法。

感谢所有抽出时间阅读或回复本文的人。