我有一个可折叠的D3树,我已修改它以使用Angular 4.一切正常。
脚本读取URL中的Params,获取参数中的ID并打开图形直到请求的节点(附件)
URL:
/asset-graph?id=27
在这种情况下,ID 27是图表中的资产3。
我需要更改颜色或突出显示图表中请求的节点。
以下是我提出的代码:
.style('fill', (d: any) => {
// debugger
let id;
if (d._children == null && d.children == null) {
id = d.data.id;
}
if (!UtilService.empty(d.children)) {
id = d.children[0].data.id;
}
if (!UtilService.empty(d._children)) {
id = d._children[0].data.id;
}
if (!UtilService.empty(id)) {
if ( String(this.parentId) === String(id) ) {
return 'red';
}
}
});
此代码工作正常,结果是(附加) - 我选择了节点2.2
以下是问题
如果节点是父节点,则相同的代码显示错误的突出显示标签:(附加)
在这里,我将URL中的参数更改为资产3,但资产2已突出显示。
这是为typescript重新编写的代码。
...
ngOnInit() {
this.svg = this.graphHelper.setUp(
this.d3,
this.width,
this.height,
this.margin);
if ( !UtilService.empty(this.treeData) ) {
this.root = this.d3.hierarchy(this.treeData, (d: any) => {
return d.children;
});
this.root.x0 = this.height / 2;
this.root.y0 = 0;
[this.root].forEach(this.collapse);
this.update(this.root);
}
// this.parentId: Coming from params in URL
this.find (this.root, this.parentId);
}
collapse = (d: any) => {
if (d.children) {
d._children = d.children;
d._children.forEach((d1) => {d1.parent = d; this.collapse(d1); });
d._children.forEach(this.collapse);
d.children = null;
}
}
find(d, id) {
if (String(d.data.id) === id) {
while (d.parent) {
d = d.parent;
this.click(d, 'auto'); // if found open its parent
}
return;
}
// recursively call find function on its children
if (d.children) {
d.children.forEach( (da) => { this.find(da, id); });
} else if (d._children) {
d._children.forEach( (da) => { this.find(da, id); });
}
}
public update(source) {
this.treemap = this.d3.tree().size([this.height, this.width]);
// Assigns the x and y position for the nodes
this.treeData = this.treemap(this.root);
// Compute the new tree layout.
const nodes = this.treeData.descendants();
const links = this.treeData.descendants().slice(1);
// Normalize for fixed-depth.
nodes.forEach((d) => { d.y = d.depth * 180; });
// ****************** Nodes section ***************************
// Update the nodes...
const node = this.svg.selectAll('g.node')
.data(nodes, (d: any) => {
return d.id || ( d.id = ++this.i ); }
);
// Enter any new modes at the parent's previous position.
const nodeEnter = node.enter().append('g')
.attr('class', 'node')
.attr('transform', (d) => {
return 'translate(' + source.y0 + ',' + source.x0 + ')';
})
.on('click', this.click);
// Add Circle for the nodes
nodeEnter.append('circle')
.attr('class', 'node')
.attr('r', 1e-6)
.style('fill', (d: any) => {
return d._children ? 'lightsteelblue' : '#fff';
});
// Add labels for the nodes
nodeEnter.append('a')
.append('text')
.attr('dy', '.35em')
.attr('x', (d: any) => {
return d.children || d._children ? -13 : 13;
})
.attr('text-anchor', (d: any) => {
return d.children || d._children ? 'end' : 'start';
})
.text( (d: any) => {
return `${d.data.asset_name.length > LIMIT_LENGTH ? d.data.asset_name.substr(0, LIMIT_LENGTH) + '...' : d.data.asset_name}`;
});
// UPDATE
const nodeUpdate = nodeEnter.merge(node);
// Transition to the proper position for the node
nodeUpdate.transition()
.duration(this.duration)
.attr('transform', (d) => {
return 'translate(' + d.y + ',' + d.x + ')';
});
// Update the node attributes and style
nodeUpdate.select('circle.node')
.attr('r', 4.5)
.style('fill', (d: any) => {
return d._children ? 'lightsteelblue' : '#fff';
})
.attr('cursor', 'pointer');
// Remove any exiting nodes
const nodeExit = node.exit().transition()
.duration(this.duration)
.attr('transform', (d) => {
return 'translate(' + source.y + ',' + source.x + ')';
})
.remove();
// On exit reduce the node circles size to 0
nodeExit.select('circle')
.attr('r', 1e-6);
// On exit reduce the opacity of text labels
nodeExit.select('text')
.style('fill-opacity', 1e-6);
// ****************** links section ***************************
// Update the links...
const link = this.svg.selectAll('path.link')
.data(links, (d: any) => {
return d.id;
});
// Enter any new links at the parent's previous position.
const linkEnter = link.enter().insert('path', 'g')
.attr('class', 'link')
.attr('d', (d: any) => {
const o = {x: source.x0, y: source.y0};
return this.diagonal(o, o);
});
// UPDATE
const linkUpdate = linkEnter.merge(link);
// Transition back to the parent element position
linkUpdate.transition()
.duration(this.duration)
.attr('d', (d: any) => {
return this.diagonal(d, d.parent);
});
// Remove any exiting links
const linkExit = link.exit().transition()
.duration(this.duration)
.attr('d', (d: any) => {
const o = {x: source.x, y: source.y};
return this.diagonal(o, o);
})
.remove();
// Store the old positions for transition.
nodes.forEach((d: any) => {
d.x0 = d.x;
d.y0 = d.y;
});
}
diagonal(s, d) {
const path = `M ${s.y} ${s.x}
C ${(s.y + d.y) / 2} ${s.x},
${(s.y + d.y) / 2} ${d.x},
${d.y} ${d.x}`;
return path;
}
click = (d, type) => {
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
this.update(d);
this.clickMe(d.data.id, type);
}
clickMe = (d, type?) => {
if (type !== 'auto') {
this.graphSrv.callServer(d);
this.updateLoader = true;
}
this.callback = d;
}
}