Unsubscribing from observable on CoarseGraph Component destroy
Change-Id: I99c59e9c8355edccaca108534d14a1e0901bcc45
diff --git a/src/app/service-graph/components/coarse/coarse.component.scss b/src/app/service-graph/components/coarse/coarse.component.scss
index f2497a9..1cf8f3c 100644
--- a/src/app/service-graph/components/coarse/coarse.component.scss
+++ b/src/app/service-graph/components/coarse/coarse.component.scss
@@ -20,7 +20,7 @@
.node > rect {
stroke: $color-accent;
- fill: $background-light-color;
+ fill: $background-color;
}
.node > text {
diff --git a/src/app/service-graph/components/coarse/coarse.component.ts b/src/app/service-graph/components/coarse/coarse.component.ts
index c595cf1..566698c 100644
--- a/src/app/service-graph/components/coarse/coarse.component.ts
+++ b/src/app/service-graph/components/coarse/coarse.component.ts
@@ -4,37 +4,72 @@
import {IXosServiceGraphStore} from '../../services/graph.store';
import {IXosServiceGraph, IXosServiceGraphNode, IXosServiceGraphLink} from '../../interfaces';
import {XosServiceGraphConfig as config} from '../../graph.config';
+import {IXosDebouncer} from '../../../core/services/helpers/debounce.helper';
+import {Subscription} from 'rxjs';
class XosCoarseTenancyGraphCtrl {
- static $inject = ['$log', 'XosServiceGraphStore'];
+ static $inject = [
+ '$log',
+ 'XosServiceGraphStore',
+ 'XosDebouncer'
+ ];
public graph: IXosServiceGraph;
+ private CoarseGraphSubscription: Subscription;
private svg;
private forceLayout;
private linkGroup;
private nodeGroup;
+ // debounce functions
+ private renderGraph;
+
constructor (
private $log: ng.ILogService,
- private XosServiceGraphStore: IXosServiceGraphStore
+ private XosServiceGraphStore: IXosServiceGraphStore,
+ private XosDebouncer: IXosDebouncer
) {
- this.XosServiceGraphStore.getCoarse()
- .subscribe((res: IXosServiceGraph) => {
- // id there are no data, do nothing
- if (!res.nodes || res.nodes.length === 0 || !res.links || res.links.length === 0) {
- return;
- }
- this.graph = res;
- this.setupForceLayout(res);
- this.renderNodes(res.nodes);
- this.renderLinks(res.links);
- this.forceLayout.start();
- });
+ }
+
+ $onInit() {
+ this.renderGraph = this.XosDebouncer.debounce(this._renderGraph, 500, this);
+
+ this.CoarseGraphSubscription = this.XosServiceGraphStore.getCoarse()
+ .subscribe(
+ (res: IXosServiceGraph) => {
+ // id there are no data, do nothing
+ if (!res.nodes || res.nodes.length === 0 || !res.links || res.links.length === 0) {
+ return;
+ }
+ this.$log.debug(`[XosCoarseTenancyGraph] Coarse Event and render`, res);
+ this.graph = res;
+ this.renderGraph();
+ },
+ err => {
+ this.$log.error(`[XosCoarseTenancyGraph] Coarse Event error`, err);
+ });
this.handleSvg();
+ this.setupForceLayout();
+
+ $(window).on('resize', () => {
+ this.setupForceLayout();
+ this.renderGraph();
+ });
+ }
+
+ $onDestroy() {
+ this.CoarseGraphSubscription.unsubscribe();
+ this.XosServiceGraphStore.dispose();
+ }
+
+ private _renderGraph() {
+ this.addNodeLinksToForceLayout(this.graph);
+ this.renderNodes(this.graph.nodes);
+ this.renderLinks(this.graph.links);
}
private getSvgDimensions(): {width: number, heigth: number} {
@@ -74,7 +109,7 @@
});
}
- private setupForceLayout(data: IXosServiceGraph) {
+ private setupForceLayout() {
const tick = () => {
this.nodeGroup.selectAll('g.node')
@@ -84,23 +119,28 @@
this.linkGroup.selectAll('line')
.attr({
- x1: l => l.source.x,
- y1: l => l.source.y,
- x2: l => l.target.x,
- y2: l => l.target.y,
+ x1: l => l.source.x || 0,
+ y1: l => l.source.y || 0,
+ x2: l => l.target.x || 0,
+ y2: l => l.target.y || 0,
});
};
const svgDim = this.getSvgDimensions();
this.forceLayout = d3.layout.force()
.size([svgDim.width, svgDim.heigth])
- .nodes(data.nodes)
- .links(data.links)
.linkDistance(config.force.linkDistance)
.charge(config.force.charge)
.gravity(config.force.gravity)
.on('tick', tick);
}
+ private addNodeLinksToForceLayout(data: IXosServiceGraph) {
+ this.forceLayout
+ .nodes(data.nodes)
+ .links(data.links)
+ .start();
+ }
+
private getSiblingTextBBox(contex: any /* D3 this */) {
return d3.select(contex.parentNode).select('text').node().getBBox();
}
@@ -108,17 +148,23 @@
private renderNodes(nodes: IXosServiceGraphNode[]) {
const self = this;
const node = this.nodeGroup
- .selectAll('rect')
- .data(nodes);
+ .selectAll('g.node')
+ .data(nodes, n => n.id);
+ const svgDim = this.getSvgDimensions();
const entering = node.enter()
.append('g')
.attr({
class: 'node',
+ transform: `translate(${svgDim.width / 2}, ${svgDim.heigth / 2})`
})
.call(this.forceLayout.drag)
- .on('mousedown', () => { d3.event.stopPropagation(); })
- .on('mouseup', (d) => { d.fixed = true; });
+ .on('mousedown', () => {
+ d3.event.stopPropagation();
+ })
+ .on('mouseup', (d) => {
+ d.fixed = true;
+ });
entering.append('rect')
.attr({
@@ -131,6 +177,7 @@
'text-anchor': 'middle'
})
.text(n => n.label);
+ // .text(n => `${n.id} - ${n.label}`);
const existing = node.selectAll('rect');
@@ -150,17 +197,21 @@
private renderLinks(links: IXosServiceGraphLink[]) {
const link = this.linkGroup
- .selectAll('rect')
- .data(links);
+ .selectAll('line')
+ .data(links, l => l.id);
- const entering = link.enter()
- .append('g')
- .attr({
- class: 'link',
- });
+ const entering = link.enter();
+ // NOTE do we need to have groups?
+ // .append('g')
+ // .attr({
+ // class: 'link',
+ // });
entering.append('line')
- .attr('marker-start', 'url(#arrow)');
+ .attr({
+ class: 'link',
+ 'marker-start': 'url(#arrow)'
+ });
}
}
diff --git a/src/app/service-graph/services/graph.store.ts b/src/app/service-graph/services/graph.store.ts
index 9636bb2..821da41 100644
--- a/src/app/service-graph/services/graph.store.ts
+++ b/src/app/service-graph/services/graph.store.ts
@@ -1,5 +1,5 @@
import * as _ from 'lodash';
-import {Observable, BehaviorSubject} from 'rxjs';
+import {Observable, BehaviorSubject, Subscription} from 'rxjs';
import {IXosModelStoreService} from '../../datasources/stores/model.store';
import {
IXosServiceGraph, IXosServiceModel, IXosTenantModel, IXosCoarseGraphData,
@@ -9,6 +9,7 @@
export interface IXosServiceGraphStore {
get(): Observable<IXosServiceGraph>;
getCoarse(): Observable<IXosServiceGraph>;
+ dispose(): void;
}
export class XosServiceGraphStore implements IXosServiceGraphStore {
@@ -36,8 +37,8 @@
private handleData;
// datastore
- private ServiceObservable: Observable<any>;
- private TenantObservable: Observable<any>;
+ private ServiceSubscription: Subscription;
+ private TenantSubscription: Subscription;
constructor (
private $log: ng.ILogService,
@@ -50,27 +51,25 @@
// we want to have a quiet period of 500ms from the last event before doing anything
this.handleData = this.XosDebouncer.debounce(this._handleData, 500, this, false);
- this.ServiceObservable = this.XosModelStore.query('Service', '/core/services');
- this.TenantObservable = this.XosModelStore.query('Tenant', '/core/tenants');
// observe models and populate graphData
- this.ServiceObservable
+ this.ServiceSubscription = this.XosModelStore.query('Service', '/core/services')
.subscribe(
(res) => {
this.combineData(res, 'services');
},
(err) => {
- this.$log.error(err);
+ this.$log.error(`[XosServiceGraphStore] graphData Observable: `, err);
}
);
- this.TenantObservable
+ this.TenantSubscription = this.XosModelStore.query('Tenant', '/core/tenants')
.subscribe(
(res) => {
this.combineData(res, 'tenants');
},
(err) => {
- this.$log.error(err);
+ this.$log.error(`[XosServiceGraphStore] graphData Observable: `, err);
}
);
@@ -91,6 +90,12 @@
);
}
+ public dispose() {
+ // cancel subscriptions from observables
+ this.ServiceSubscription.unsubscribe();
+ this.TenantSubscription.unsubscribe();
+ }
+
public get() {
return this.d3FineGrainedGraph.asObservable();
}
@@ -118,20 +123,19 @@
});
}
- private getNodeIndexById(id: number, nodes: IXosServiceModel[]) {
+ private getCoarseNodeIndexById(id: number, nodes: IXosServiceModel[]) {
return _.findIndex(nodes, {id: id});
}
private graphDataToCoarseGraph(data: IXosCoarseGraphData) {
-
// TODO find how to bind source/target by node ID and not by position in array (ask Simon?)
const links: IXosServiceGraphLink[] = _.chain(data.tenants)
.filter((t: IXosTenantModel) => t.kind === 'coarse')
.map((t: IXosTenantModel) => {
return {
id: t.id,
- source: this.getNodeIndexById(t.provider_service_id, data.services),
- target: this.getNodeIndexById(t.subscriber_service_id, data.services),
+ source: this.getCoarseNodeIndexById(t.provider_service_id, data.services),
+ target: this.getCoarseNodeIndexById(t.subscriber_service_id, data.services),
model: t
};
})