Unsubscribing from observable on CoarseGraph Component destroy

Change-Id: I99c59e9c8355edccaca108534d14a1e0901bcc45
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)'
+      });
   }
 }