Angular 模板中可观察对象上的 ObjectUnsubscribedErrorImpl

2024-04-06

我正在使用 Angular 11,并且正在使用以下命令访问组件模板中的可观察对象async pipe.

路线的第一次加载,一切都工作得很好。没有错误。当我离开该页面并返回时,出现以下错误:

组件模板:

成分

import { Component, OnDestroy, OnInit } from '@angular/core';
import { Observable, Subject } from 'rxjs';

import { FullMapViewService } from '../services/full-map-view.service';
import { RISLayerConfigResponse } from '@RM/interfaces';
import { takeUntil } from 'rxjs/operators';

@Component({
  selector: 'RM-full-map-view',
  templateUrl: './full-map-view.component.html',
  styleUrls: ['./full-map-view.component.scss']
})
export class FullMapViewComponent implements OnInit, OnDestroy {
  layers$: Observable<RISLayerConfigResponse>;
  destroyed$: Subject<boolean> = new Subject();
  constructor(private fullMapViewService: FullMapViewService) {}

  ngOnInit(): void {
    this.fullMapViewService.setParamsRequiredForRIS();
    this.fullMapViewService.initializeRISLayerCreationService();
    this.layers$ = this.fullMapViewService
      .getLayersForAllProjects()
      .pipe(takeUntil(this.destroyed$));
  }

  ngOnDestroy() {
    this.destroyed$.next(true);
  }
}

全地图视图.service.ts

import { DEPLOYMENT_PATH, SET_FROM_SERVER } from '@XYZ/RIS';
import {
  DataSet,
  DatasetsAndLayerConfig,
  RISLayerConfigResponse,
  RISLayerSettingsWithKind,
  Layer,
  LayerConfig,
  UpdateViewVCS
} from '@XYZ/interfaces';
import { Injectable, OnDestroy } from '@angular/core';
import { Observable, Subscription } from 'rxjs';

import { API_PATHS } from '../../api-paths';
import { BaseAPIService } from '@XYZ/core';
import { ClauseGenerationUtility } from '../../utils/clause-generation/clause-generation.util';
import { RISLayerCreationService } from '@ABC-innersource/RIS-canvas';
import { LAYER_COLOR_PALLET } from '../../services/services.constant';
import { map } from 'rxjs/operators';

@Injectable()
export class FullMapViewService implements OnDestroy {
  layersMappingConfiguration: {};
  layers: LayerConfig;
  private clauseGenerator = new ClauseGenerationUtility();
  private addUpdateVCSForKindSubscriptions: Subscription[];
  private initializeRISLayerCreationServiceSubscription: Subscription;
  private deploymentUrl: string;
  private appKey: string;
  private ABCDataPartitionId: string;
  private sToken: string;

  constructor(
    private baseAPIService: BaseAPIService,
    private layerCreationService: RISLayerCreationService
  ) {}

  // eslint-disable-next-line max-lines-per-function
  getLayersForAllProjects(): Observable<RISLayerConfigResponse> {
    return this.baseAPIService
      .get(API_PATHS.LAYERS.GET_LAYERS + '/projects/all')
      .pipe(
        map((res: DatasetsAndLayerConfig) => {
          return res;
        }),
        // eslint-disable-next-line max-lines-per-function
        map((datasetsAndLayerConfig: DatasetsAndLayerConfig) => {
          const datasets = [...datasetsAndLayerConfig.datasets];
          const notConfiguredKinds = [
            ...datasetsAndLayerConfig.layerConfig.notConfiguredKinds
          ];
          const notConfiguredKindsLayers = this.getNonConfiguredKindsLayers(
            notConfiguredKinds
          );
          const layers = this.combineLayersAndNotConfiguredKindsLayers(
            datasetsAndLayerConfig.layerConfig.layerConfig,
            notConfiguredKindsLayers
          );
          const kindsLayersHashmap = this.getKindsLayersHashmap(layers);
          const layersByDatasets = datasets
            .map((dataset: DataSet) => {
              return {
                ...this.updateLayersWithDatasetNameAndClauses(
                  kindsLayersHashmap,
                  dataset
                )
              };
            })
            .filter((layer) => {
              return Object.keys(layer).length !== 0;
            })
            .map((layer, index) => {
              return {
                ...this.assignColourToLayer(layer, index)
              };
            });
          return {
            layerConfig: layersByDatasets,
            notConfiguredKinds: []
          };
        })
      );
  }

  setParamsRequiredForRIS(): void {
    this.sToken = SET_FROM_SERVER;
    this.deploymentUrl = DEPLOYMENT_PATH;
    this.appKey = SET_FROM_SERVER;
    this.ABCDataPartitionId = SET_FROM_SERVER;
  }

  initializeRISLayerCreationService(): void {
    this.initializeRISLayerCreationServiceSubscription = this.layerCreationService
      .initialize(
        this.sToken,
        this.deploymentUrl,
        this.appKey,
        this.ABCDataPartitionId
      )
      .subscribe();
  }

  ngOnDestroy(): void {
    this.initializeRISLayerCreationServiceSubscription.unsubscribe();
    this.addUpdateVCSForKindSubscriptions.forEach(
      (subscription: Subscription) => {
        subscription.unsubscribe();
      }
    );
  }

  private updateLayersWithDatasetNameAndClauses(
    kindsLayersHashmap: Map<string, RISLayerSettingsWithKind>,
    dataset: DataSet
  ): RISLayerSettingsWithKind {
    const currentDataset = { ...dataset };
    const datasetKind = this.generateKindFromDataset(currentDataset);
    const layer = kindsLayersHashmap.get(datasetKind);
    const queryRef = this.getFormattedQuery(
      currentDataset.dataSetDefinition.queryDefinition.queryRef
    );
    const clause = this.clauseGenerator.generateClause(queryRef);

    if (!layer) {
      return undefined;
    }

    layer.name = currentDataset.name;
    layer.tableInfo.where = clause;
    return JSON.parse(JSON.stringify(layer));
  }

  private generateKindFromDataset(dataset: DataSet): string {
    const currentDataset = { ...dataset };
    const datasetQueryDefinition =
      currentDataset.dataSetDefinition.queryDefinition;
    return `${datasetQueryDefinition.authority}:${datasetQueryDefinition.source}:${datasetQueryDefinition.entity}:${datasetQueryDefinition.version}`;
  }

  private getKindsLayersHashmap(
    layers: RISLayerSettingsWithKind[]
  ): Map<string, RISLayerSettingsWithKind> {
    const kindsLayersHashmap = new Map();
    const allLayers = [...layers];
    allLayers.forEach((layer: RISLayerSettingsWithKind) => {
      kindsLayersHashmap.set(layer.kind, layer);
    });
    return kindsLayersHashmap;
  }

  private getNonConfiguredKindsLayers(
    kinds: string[]
  ): RISLayerSettingsWithKind[] {
    const notConfiguredKindsLayers: RISLayerSettingsWithKind[] = [];
    kinds.forEach((kind) => {
      const layer: RISLayerSettingsWithKind[] = this.layerCreationService.getLayerInfoByKindName(
        kind
      ) as RISLayerSettingsWithKind[];
      if (layer.length > 0) {
        layer[0].kind = kind;
        notConfiguredKindsLayers.push(layer[0]);
        this.addUpdateRISLayerInVCS({ kind: kind, configuration: layer[0] });
      }
    });
    return notConfiguredKindsLayers;
  }

  private addUpdateRISLayerInVCS(layer: Layer): void {
    const currentLayer = { ...layer };
    const updateViewPayload: UpdateViewVCS = {
      control: 'RIS',
      definition: [{ ...currentLayer.configuration }]
    };
    this.addUpdateVCSForKind(currentLayer.kind, updateViewPayload);
  }

  private addUpdateVCSForKind(kind: string, payload: UpdateViewVCS): void {
    const subscription = this.baseAPIService
      .post(
        `${API_PATHS.CONFIG.VIEW.UPDATE_RIS_VIEW_CONFIG}`.replace(
          '${kind}',
          kind
        ),
        payload
      )
      .subscribe();
    this.addUpdateVCSForKindSubscriptions.push(subscription);
  }

  private combineLayersAndNotConfiguredKindsLayers(
    layers: RISLayerSettingsWithKind[],
    notConfiguredKindsLayers: RISLayerSettingsWithKind[]
  ): RISLayerSettingsWithKind[] {
    const allLayers = [...layers];
    const allNotConfiguredKindsLayers = [...notConfiguredKindsLayers];
    return [...allLayers, ...allNotConfiguredKindsLayers];
  }

  private getFormattedQuery(query: string): string {
    let formattedQuery = '';
    if (
      this.clauseGenerator.hasAndOperator(query) ||
      this.clauseGenerator.hasOrOperator(query)
    ) {
      formattedQuery = this.clauseGenerator.isWrappedWithRoundBrackets(query)
        ? query
        : `(${query})`;
      return formattedQuery;
    }
    return formattedQuery;
  }

  private assignColourToLayer(
    layer: RISLayerSettingsWithKind,
    index: number
  ): RISLayerSettingsWithKind {
    const colors = LAYER_COLOR_PALLET;
    const currentLayer = JSON.parse(JSON.stringify(layer));
    currentLayer.style.rules[0].style.fillColor = colors[index];
    currentLayer.style.rules[0].style.borderColor = '#000';
    return currentLayer;
  }
}

例如路线 B ​​是我包含可观察到的组件 A ---> B 可观察到的负载非常好。 B ----> A 和 A ----> B 可观察到的抛出以下错误。

ObjectUnsubscribedErrorImpl {message: "object unsubscribed", name: "ObjectUnsubscribedError"}
message: "object unsubscribed"
name: "ObjectUnsubscribedError"

完整的堆栈跟踪快照如下所示:

    core.js:6162 ERROR ObjectUnsubscribedErrorImpl {message: "object unsubscribed", name: "ObjectUnsubscribedError"}message: "object unsubscribed"name: "ObjectUnsubscribedError"__proto__: Error

defaultErrorLogger  @   core.js:6162
handleError @   core.js:6210
(anonymous) @   core.js:29503
invoke  @   zone-evergreen.js:364
run @   zone-evergreen.js:123
runOutsideAngular   @   core.js:28439
tick    @   core.js:29503
(anonymous) @   core.js:29372
invoke  @   zone-evergreen.js:364
onInvoke    @   core.js:28510
invoke  @   zone-evergreen.js:363
run @   zone-evergreen.js:123
run @   core.js:28394
next    @   core.js:29371
schedulerFn @   core.js:25848
__tryOrUnsub    @   Subscriber.js:183
next    @   Subscriber.js:122
_next   @   Subscriber.js:72
next    @   Subscriber.js:49
next    @   Subject.js:39
emit    @   core.js:25838
checkStable @   core.js:28447
onLeave @   core.js:28560
onInvokeTask    @   core.js:28504
invokeTask  @   zone-evergreen.js:398
runTask @   zone-evergreen.js:167
invokeTask  @   zone-evergreen.js:480
invokeTask  @   zone-evergreen.js:1621
globalZoneAwareCallback @   zone-evergreen.js:1658
load (async)        
customScheduleGlobal    @   zone-evergreen.js:1773
scheduleTask    @   zone-evergreen.js:385
onScheduleTask  @   zone-evergreen.js:272
scheduleTask    @   zone-evergreen.js:378
scheduleTask    @   zone-evergreen.js:210
scheduleEventTask   @   zone-evergreen.js:236
(anonymous) @   zone-evergreen.js:1928
(anonymous) @   http.js:1805
_trySubscribe   @   Observable.js:42
subscribe   @   Observable.js:28
call    @   catchError.js:14
subscribe   @   Observable.js:23
call    @   catchError.js:14
subscribe   @   Observable.js:23
innerSubscribe  @   innerSubscribe.js:67
_innerSub   @   mergeMap.js:57
_tryNext    @   mergeMap.js:51
_next   @   mergeMap.js:34
next    @   Subscriber.js:49
(anonymous) @   subscribeToArray.js:3
_trySubscribe   @   Observable.js:42
subscribe   @   Observable.js:28
call    @   mergeMap.js:19
subscribe   @   Observable.js:23
call    @   filter.js:13
subscribe   @   Observable.js:23
call    @   map.js:16
subscribe   @   Observable.js:23
call    @   map.js:16
subscribe   @   Observable.js:23
call    @   map.js:16
subscribe   @   Observable.js:23
createSubscription  @   common.js:4224
_subscribe  @   common.js:4305
transform   @   common.js:4292
ɵɵpipeBind1 @   core.js:25718
FullMapViewComponent_Template   @   full-map-view.component.html:2
executeTemplate @   core.js:9549
refreshView @   core.js:9418
refreshComponent    @   core.js:10584
refreshChildComponents  @   core.js:9215
refreshView @   core.js:9468
refreshEmbeddedViews    @   core.js:10538
refreshView @   core.js:9442
refreshEmbeddedViews    @   core.js:10538
refreshView @   core.js:9442
refreshComponent    @   core.js:10584
refreshChildComponents  @   core.js:9215
refreshView @   core.js:9468
renderComponentOrTemplate   @   core.js:9532
tickRootContext @   core.js:10758
detectChangesInRootView @   core.js:10783
detectChanges   @   core.js:22751
tick    @   core.js:29491
(anonymous) @   core.js:29372
invoke  @   zone-evergreen.js:364
onInvoke    @   core.js:28510
invoke  @   zone-evergreen.js:363
run @   zone-evergreen.js:123
run @   core.js:28394
next    @   core.js:29371
schedulerFn @   core.js:25848
__tryOrUnsub    @   Subscriber.js:183
next    @   Subscriber.js:122
_next   @   Subscriber.js:72
next    @   Subscriber.js:49
next    @   Subject.js:39
emit    @   core.js:25838
checkStable @   core.js:28447
onHasTask   @   core.js:28527
hasTask @   zone-evergreen.js:419
_updateTaskCount    @   zone-evergreen.js:440
_updateTaskCount    @   zone-evergreen.js:263
runTask @   zone-evergreen.js:184
drainMicroTaskQueue @   zone-evergreen.js:569
invokeTask  @   zone-evergreen.js:484
invokeTask  @   zone-evergreen.js:1621
globalZoneAwareCallback @   zone-evergreen.js:1647

如果你看到,FullMapViewComponent_Template @ full-map-view.component.html:2提到模板上可观察的问题。

我不确定如何处理这个问题。该模板位于路线 B ​​上。


我搜索了那些地方对象取消订阅错误 https://github.com/ReactiveX/rxjs/search?q=ObjectUnsubscribedError被扔在rxjs https://github.com/ReactiveX/rxjsgithub 项目并尝试在实际抛出此错误时获取见解。我可以在以下位置找到最好的见解this https://github.com/ReactiveX/rxjs/blob/04ceaa57b0cf93564dee01e38ddcfb208aaa2b08/spec/Subject-spec.ts#L400测试。我尝试总结一下我如何理解这种行为:

当您直接取消订阅某个主题时,该主题将被关闭。每当您尝试重新订阅它时,它都会抛出 ObjectUnsubscribedError。

众所周知,这意味着您很可能保留您的主题(为您服务),尽管您的组件被丢弃并取消订阅。当您重新路由到组件时,它会尝试再次订阅,然后抛出错误。我从上面的链接测试中获取了一个最小的可重现示例:

const { Subject } = rxjs;

const subject = new Subject();

subject.subscribe()

subject.next("foo")
subject.unsubscribe()

try {
  subject.subscribe()
} catch (e) {
  console.error("the error: ", e)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.3/rxjs.umd.min.js"></script>

现在我们知道如何抛出错误,可能的解决方案可能是仅unsubscribe on a subscription在您的组件中,然后是新组件特定主题中的值。

伪示例代码:

// Your subscription on wich you can unsubscribe
private copySubjectSubscription;
// This subject is used in your component with the async pipe
public readonly copySubject = new Subject();

constructor(serviceWithSubject) {
  this.copySubjectSubscription =
    serviceWithSubject.subject.subscribe(e => copySubject.next())
}

ngOnDestroy() {
  this.copySubjectSubscription.unsubscribe();
}

由于我不喜欢您的代码并且时间也有限,您可能会找到一种更优雅的解决方案来复制主题及其值,而无需直接取消订阅。

我个人用 rxjs 工作了很多,从来没有遇到过这个问题。也许我总是可以通过不同的设计方法来避免这种行为,因为我总是尝试创建和销毁Observables在组件中。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Angular 模板中可观察对象上的 ObjectUnsubscribedErrorImpl 的相关文章

  • 如何限制 Chrome 中的最大文本区域宽度和高度或如何禁用文本区域调整大小

    Chrome 允许通过在右下角添加文本区域来调整文本区域的大小 但有时这种移动可能会破坏页面的设计 所以我想知道如何限制该操作的最大和最小宽度 即如何完全禁用该功能和thml javascript css在页面上 您可以使用 resize
  • 在 ES5 中创建自定义元素 v1,而不是 ES6

    现在 如果您严格遵循自定义元素规范 v1 https html spec whatwg org multipage custom elements html custom elements 无法在不支持类的浏览器中使用自定义元素 有没有办法
  • ECMAScript 6 类中的 getter 和 setter 有何用途?

    我对 ECMAScript 6 类中 getter 和 setter 的意义感到困惑 什么目的 下面是我参考的一个例子 class Employee constructor name this name name doWork return
  • jQuery 在附加元素后立即返回 div 元素的高度 0

    我有一个浮动 div 最初没有内容 我使用 jQuery 将一组元素附加到 div 然后立即调用原始 div 的 height 方法 我添加的元素在样式表中具有定义的最小高度 而浮动 div 则没有 问题是 当我在原始 div 上调用 he
  • 如何格式化折线图谷歌图表材料上的轴?

    我在格式化材料图表的轴时遇到问题 Using classic line chart if I would like to format my vertical axis with a dollar sign I would do vAxes
  • 从本地 html/javascript 网站插入 mySQL 数据库

    我正在尝试做什么 我的程序的目的是插入数据local HTML JS网站变成online 非本地 mySQL数据库 到目前为止我尝试过的 我试图用来实现此目的的原始方法是让我的本地网站使用 javascript 通过在线发布数据PHP文件
  • linkedin js 如何是有效的 javascript

    LinkedIn Javascript 集成是通过以下方式完成的 我不明白 这怎么是一个有效的javascript 为什么 api key 没有被引用 脚本标签的主体带有src永远不会被执行 但是 加载的脚本可以像访问任何其他元素的内容一样
  • 如何使用 javascript 将我的域名字母大写?

    假设我的域名是www hello com 如何使用 jQuery JavaScript 使浏览器的 URL 栏看起来像 www HELLO com 您无法更改浏览器地址栏中显示的内容 这是一项基本的安全功能 您可以使您的域名全部大写 并将页
  • 使用 Angular 4 将新行添加到 mat-table 中

    如何从输入字段手动将新行添加到 Angular Material 表中 请看这张图片 如果我添加状态名称和状态代码 它应该出现在下表中 请帮助我如何使用 Angular 4 来实现它 我整理了一个简单的示例 应该可以很好地提示您从哪里开始
  • FileReader 在 Ionic 2 中未触发 onloadend

    我正在尝试使用 cordova file plugin 读取本地文件 目前我可以读取本地目录的内容并选择单个文件 但我在获取文件内容时遇到问题 这是我的函数 从列表中选择文件后单击按钮即可调用该函数 import window resolv
  • 测试 - 存根服务方法未定义

    我已经在非常简单的代码上编写了一个非常简单的测试 但由于某种原因存根服务方法未定义 当我使用 Jasmine Spy 时 它可以工作 但对于这样一个简单的任务 有人可以解释一下为什么会发生这种情况吗 我删除了 import 语句只是为了减少
  • 数字和文本列的垫排序问题

    我有角度材料数据源 角度材料版本是 5 0 3 排序正在进行中 但是对于某些列 它的排序不正确 那里有数字和文字 例如 排序结果如 XXX 1 1tesxt 1 OPD OXD 12
  • 使标签充当输入按钮

    我怎样才能做一个 a href http test com tag test Test a 就像表单按钮一样 通过充当表单按钮 我的意思是 当单击链接执行操作时method get 或 post 以便能够通过 get 或 post 捕获它
  • Angular 5 中使用 rxjs 进行持久订阅

    我对 Angular 5 中的 rxjs 仍然有点陌生 并且很难表达我的问题 我仍然希望得到一些提示 我经常会得到相同的设置 多个组件显示相同的数据 访问数据的单个服务 现在通过 Observables 接收数据时我有 2 个选择 a 订阅
  • 如何使用 ReactJS 使表中的列可以以两种方式排序

    我正在 ReactJS 中构建一个简单的应用程序 它通过调用某个 API 来使用 JSON 数组 然后我将数组的结果填充到表中 我现在想让表的列可排序 我理想的情况是同时进行升序和降序排序 一旦我单击标题 当它按升序排序时 它应该按降序排序
  • mat-tab-group 不是 Angular 9 中的已知元素

    我正在使用 Angular 9 和 Angular Material 9 2 4 我正在尝试使用mat tab 组在我的 component html 中 但我不断收到错误 mat tab group is not a known elem
  • 单击 html 中的按钮后如何从 javascript 函数写入文件

    我正在尝试编写真正基本的代码 在 html 文件上按下按钮后 通过 JavaScript 函数在本地写入 txt 文件 这不可能吗 我可以仅使用 javascript 文件写入文件 但在尝试同时使用两者时则不能
  • 数组长度未定义[关闭]

    这个问题不太可能对任何未来的访客有帮助 它只与一个较小的地理区域 一个特定的时间点或一个非常狭窄的情况相关 通常不适用于全世界的互联网受众 为了帮助使这个问题更广泛地适用 访问帮助中心 help reopen questions 我试图按如
  • 如何在控制台中隐藏日志消息的来源?

    当将消息输出到控制台时 还会显示源代码 在 Chrome 开发者工具中 它位于右侧 console log Foo Source Foo test js 1 Output 但是 在某些网站上 会显示消息without正在显示的源 例如Fac
  • 如何将函数导入到Vue组件中?

    我正在尝试将单个函数导入到我的 Vue 组件中 我为我的函数创建了一个单独的 js 文件 randomId js exports randomId gt My function 在我的 Vue 组件中 我导入了 Random js let

随机推荐