I am using Angular 11 and I am accessing an observable in the template of my component with async pipe.
First load of the route, everything works perfectly fine. No error. When I navigate away from the page and come back, I get following error:
Component Template: <RM-map *ngIf="(layers$ | async) as layers" [layers]="layers.layerConfig" showLayersPanel="true" id="RIS-map">
Component
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);
}
}
full-map-view.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;
}
}
For e.g. route B is my component containing the observable A ---> B the observable loads perfectly fine. B ----> A and again A ----> B the observable throws below error.
ObjectUnsubscribedErrorImpl {message: "object unsubscribed", name: "ObjectUnsubscribedError"}
message: "object unsubscribed"
name: "ObjectUnsubscribedError"
Full stack trace snapshot is as shown below:
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
If you see, FullMapViewComponent_Template @ full-map-view.component.html:2
mentions an issue with the observable on the template.
I am unsure how to handle this. This template is on Route B.
getLayersForAllProjects()
return? Is it possible it is returning an observable that is already completed or a subject that has been unsubscribed? – EldestngOnDestroy()
in the service? – Eldest