I am working on a collapsible tree for power bi custom visual. I want to implement a toggling functionality that would allow me to drill up or down a node when I click on it. Below I have provided my current code for rendering a tree and an other method that executes when a node is clicked. What I think is that when a node is clicked, renderTree method is called again from within click method and that results in rendering of whole tree again. And thus I am not able to see the nodes being toggled. What changes or any additionl method would I need for that. Thanks in advance.
Here is what I have tried so far but it does not give me the correct results. In this case the measured data is actuall a json tree and tree() and diagonal() have already been declared in global scope of class Visual just like so:
// Declare an interface for the links/connections
interface LinkCoords {
x: number;
y: number;
}
// Declare an interface for a tree node
interface TreeNode {
name: string;
children?: TreeNode[];
// Dynamic addition of any incoming columns from analyze by data role
[key: string]: number | string | undefined | TreeNode[];
}
// Create a tree layout
private tree = d3
.tree()
// Height and width of the separations
.nodeSize([50, 300])
// Wether or not the nodes have same parent, there should be a separation of 2 units between nodes
.separation(function separation(a, b) {
return a.parent == b.parent ? 2 : 2;
});
// Links properties
private diagonal = d3
.linkHorizontal<{}, LinkCoords>()
.x((d) => d.y)
.y((d) => d.x);
// Function that would render the tree on the canvas
private renderTree(measuredData: TreeNode) {
// Compute the hierarchy
const root = d3.hierarchy(measuredData);
// Call the tree function
this.tree(root);
// Node variable
const node = this.treeContainer
.selectAll(".node")
.data(root.descendants())
.enter()
.append("g")
.attr("transform", function (d: any) {
return "translate(" + (d.y + 20) + "," + (d.x + 307) + ")";
})
.attr("font-size", 10)
.on("click", (event, d) => this.click(d.data));
// Node properties
node
.append("rect")
.attr("width", 150)
.attr("height", 50)
.style("stroke", function (d) {
return d.children ? "#5CCE8A" : "#D45F5F";
})
.attr("fill", "grey");
// Node text properties
node
.append("text")
.attr("x", 70)
.attr("y", 25)
.attr("text-anchor", "middle")
.text(
(d) => `name: ${d.data.name}, value: ${d.data.V}, import: ${d.data.I}`
)
.attr("fill", "white");
// Create links
const link = this.treeContainer
.selectAll(".link")
.data(root.links())
.enter();
// Define the path source and target for the links
link
.append("path")
.attr("class", "link")
.attr("fill", "none")
.attr("stroke", "white")
.attr("d", (d) => {
// Define the source
const source: LinkCoords = {
x: (d.source as any).x + 332,
y: (d.source as any).y + 170,
};
// Define the target
const target: LinkCoords = {
x: (d.target as any).x + 332,
y: (d.target as any).y + 20,
};
return this.diagonal({ source, target });
})
.attr("stroke-width", "1px")
.attr("stroke-dasharray", "1, 0");
}
// Toggle the node
private click(d: any) {
if (d.children) {
// If the clicked node has children
d._children = d.children; // Hide its children
d.children = null;
} else {
// If the clicked node does not have children
d.children = d._children; // Show its children
d._children = null;
}
this.renderTree(d);
}