I am trying to build a tool with the help of Angular to visualize graphs similar to TensorBoard. My intention is to visualize the dependency graph that the build tool bazel outputs. In order to highlight the graph I want to draw a rect as a background. My current approach is to draw the nodes and edges within one group, measure the dimensions of the group and set the width and height of the rect accordingly. If I change the graph structure by expanding or minimizing a node the background rect does not update properly. I am guessing that this is because no change was made with respect to the components data and therefore the view won't get updated by Angular.
You will find the source code here: https://github.com/dprogm/graph-explorer
The template of the graph looks like this:
<svg:rect
attr.width="{{_graph.width}}"
attr.height="{{_graph.height}}" />
<svg:g #graphRef>
<svg:path *ngFor="let edge of _graph.edges"
class="edge"
[attr.d]="buildPath(edge)" />
<svg:g app-graph-node *ngFor="let node of _graph.nodes"
[node]="node"
(nodeChange)="onNodeChanged($event)"></svg:g>
</svg:g>
The #graphRef
is an ElementRef
which is used for querying the bounding box:
if(this.graphRef !== undefined) {
let graphBBox = this.graphRef.nativeElement.getBBox();
this._graph.width = graphBBox.width;
this._graph.height = graphBBox.height;
}
The main question is: Is there any better approach of measuring the size of the graph and if not how can I achieve that I get the right dimensions?
You can reproduce the issue here: https://stackblitz.com/edit/angular-ivy-mkmkm6?file=src/app/app.component.html If you click the circle it changes the size. I want that the black rect also changes its size to fit the circle.
One way is to use requestAnimationFrame()
:
import { Component, ElementRef, ViewChild, AfterViewInit, VERSION } from '@angular/core';
import { Observable } from 'rxjs';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent implements AfterViewInit {
// Background width
bgWidth = 0;
// Background height
bgHeight = 0;
// Radius of the circle
r = 100;
// Stroke width of the circle
strokeWidth = 5;
// Whether to draw a large or small circle
largeMode: Boolean = true;
@ViewChild('groupRef')
groupRef : ElementRef;
ngAfterViewInit(): void {
requestAnimationFrame(()=> this.setRectSize());
}
setRectSize(): void {
let groupBBox = this.groupRef.nativeElement.getBBox();
this.bgWidth = groupBBox.width;
this.bgHeight = groupBBox.height;
}
changeSize() : void {
this.largeMode = !this.largeMode;
if(this.largeMode) {
this.r = 100;
} else {
this.r = 50;
}
requestAnimationFrame(()=> this.setRectSize());
}
}