title: D3 Angular Tree
body: <p>I have a working D3 tree that I am trying to convert to Angular. It's a v3 tree. </p>
  
  <p>The component code is:</p>
  
  <pre><code>import { Component, OnInit, ViewChild, ElementRef, AfterViewInit, ViewEncapsulation } from '@angular/core';
  declare var d3: any;
  
  @Component({
    selector: 'app-tree-view',
    template: '&lt;div #chart id="chart"&gt;&lt;/div&gt;',
    encapsulation: ViewEncapsulation.None,
    styleUrls: ['./tree-view.component.scss']
  })
  export class TreeViewComponent implements AfterViewInit, OnInit {
  
    ngOnInit(): void {
      this.createChart();
    }
  
    constructor(){
  
    }
  
    ngAfterViewInit(): void {
      this.root.update = this.update;
      this.update(this.root);
    }
  
  private treeData: any[] = [{...data here}],
  
  tree: any;
  i = 0;
  duration = 750;
  root:any = {};
  svg:any = {};
  diagonal: any;
  
  @ViewChild('chart')
  private chartContainer: ElementRef;
  
  
  private createChart(): void {
    var margin = {top: 20, right: 120, bottom: 20, left: 120},
      width = 1560 - margin.right - margin.left,
      height = 1000 - margin.top - margin.bottom;
  
   this.tree = d3.layout.tree()
      .size([height, width]);
  
    const element = this.chartContainer.nativeElement;
  
   this.diagonal = d3.svg.diagonal().projection((d) =&gt; { return [d.y, d.x]; });
  
   this.svg = d3.select(element).append("svg")
      .attr("width", width + margin.right + margin.left)
      .attr("height", height + margin.top + margin.bottom)
    .append("g")
      .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
  
  this.root = this.treeData[0];
  this.root.x0 = height / 2;
  this.root.y0 = 0;
  
  this.hideChildren(this.root);
  
  console.log(this.root);
  
  d3.select(self.frameElement).style("height", "1000px");
  }
  
   private hideChildren(node) {
      if(node.children) {
          node._children = node.children;
          node.children = null;
          node._children.forEach((a) =&gt;  { this.hideChildren});
      }
  }
  
   private update(source) {
  
    // Compute the new tree layout.
    var nodes = this.tree.nodes(this.root).reverse(),
        links = this.tree.links(nodes);
  
    // Normalize for fixed-depth.
    nodes.forEach((d) =&gt; { d.y = d.depth * 180; });
  
    // Update the nodes…
    var node = this.svg.selectAll("g.node")
        .data(nodes, (d) =&gt; { return d.id || (d.id = ++this.i ); });
  
    // Enter any new nodes at the parent's previous position.
    var nodeEnter = node.enter().append("g")
        .attr("class", "node")
        .attr("transform", (d) =&gt; { return "translate(" + source.y0 + "," + source.x0 + ")"; })
        .on("click", this.click);
  
    nodeEnter.append("circle")
        .attr("r", 1e-6)
        .style("fill", (d) =&gt; { return d._children ? "lightsteelblue" : "#fff"; });
  
    nodeEnter.append("text")
        .attr("x", (d) =&gt; { return d.children || d._children ? -13 : 13; })
        .attr("dy", ".35em")
        .attr("text-anchor", (d) =&gt; { return d.children || d._children ? "end" : "start"; })
        .text((d) =&gt; { return d.name; })
        .style("fill-opacity", 1e-6);
  
    // Transition nodes to their new position.
    var nodeUpdate = node.transition()
        .duration(this.duration)
        .attr("transform", (d) =&gt; { return "translate(" + d.y + "," + d.x + ")"; });
  
    nodeUpdate.select("circle")
        .attr("r", 10)
        .style("fill", (d) =&gt; { return d._children ? "lightsteelblue" : "#fff"; });
  
    nodeUpdate.select("text")
        .style("fill-opacity", 1);
  
    // Transition exiting nodes to the parent's new position.
    var nodeExit = node.exit().transition()
        .duration(this.duration)
        .attr("transform", (d) =&gt; { return "translate(" + source.y + "," + source.x + ")"; })
        .remove();
  
    nodeExit.select("circle")
        .attr("r", 1e-6);
  
    nodeExit.select("text")
        .style("fill-opacity", 1e-6);
  
    // Update the links…
    var link = this.svg.selectAll("path.link")
        .data(links, (d) =&gt; { return d.target.id; });
  
    // Enter any new links at the parent's previous position.
    link.enter().insert("path", "g")
        .attr("class", "link")
        .attr("d", (d) =&gt; {
          var o = {x: source.x0, y: source.y0};
          return this.diagonal({source: o, target: o});
        });
  
    // Transition links to their new position.
    link.transition()
        .duration(this.duration)
        .attr("d", this.diagonal);
  
    // Transition exiting nodes to the parent's new position.
    link.exit().transition()
        .duration(this.duration)
        .attr("d", (d) =&gt; {
          var o = {x: source.x, y: source.y};
          return this.diagonal({source: o, target: o});
        })
        .remove();
  
    // Stash the old positions for transition.
    nodes.forEach((d) =&gt; {
      d.x0 = d.x;
      d.y0 = d.y;
    });
  
    console.log('end creating chart');
  }
  
  // Toggle children on click.
  click(d, item) {
    if (d.children) {
      d._children = d.children;
      d.children = null;
    } else {
      d.children = d._children;
      d._children = null;
    }
  
    this.update(d);
  }
  
  
  }
  </code></pre>
  
  <p>The script is compiled into the angular.json file using</p>
  
  <p><code>"scripts": ["src/scripts/d3.js"]</code></p>
  
  <p>However, when I expand a node I am getting an error of </p>
  
  <blockquote>
    <p>5core.js:14597 ERROR TypeError: this.update is not a function
        at SVGGElement.push../src/app/tree-view/tree-view.component.ts.TreeViewComponent.click
    (tree-view.component.ts:69898)
        at SVGGElement.__onclick (d3.js:1)
        at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask
    (zone.js:423)
        at Object.onInvokeTask (core.js:16147)
        at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask
    (zone.js:422)
        at Zone.push../node_modules/zone.js/dist/zone.js.Zone.runTask (zone.js:195)
        at ZoneTask.push../node_modules/zone.js/dist/zone.js.ZoneTask.invokeTask
    [as invoke] (zone.js:498)
        at invokeTask (zone.js:1744)
        at SVGGElement.globalZoneAwareCallback (zone.js:1770)</p>
  </blockquote>
  
  <p>Clearly this is as scoping issue - with<code>this</code>, but I don't know how to get around this. <code>this</code> refers to the node, not the class. </p>
  
  <p>Any help would be much appreciated</p>
  
  <p>Cheers
  KH</p>