我设法使用d3制作一个自下而上的家谱,添加,删除工作正常,但是当我尝试加载嵌套子项的初始数据超过2或者超过根时,可视化所述数据中断。
如何在给定多个嵌套的情况下获取初始化数据以进行可视化 孩子?我让它错过了什么?
当节点达到3级以上时,如何计算和设置节点之间的分隔?
我已经完成了输入,更新和退出,因为它应该我认为,这使得它可以添加,删除。 JSFiddle for only root data, with adding and deleting。
当我用超过1个孩子初始化数据时,可视化开始中断,而不是最初显示所有关系,它只显示一个关系。 The fiddle of said problem。因此:
最初加载嵌套子数据时我想要实现的是嵌套关系的可视化,因为上面的情况应该是:
数据:
{
patient_name: "Adam Farish",
reference_id: "199210291",
relationship: "",
pc_id: "121292",
consanguineous_marriage: false,
children: [{
patient_name: "Adam Father",
reference_id: "199210291",
relationship: "",
pc_id: "121292",
consanguineous_marriage: false,
children: [],
problems: [{
id: 0,
name: "cleft lip",
impact: "disease",
remarks: "Need constant monitoring."
},
{
id: 1,
name: "cleft palate",
impact: "carrier",
remarks: "Need constant monitoring."
}]
},{
patient_name: "Adam Mother",
reference_id: "199210291",
relationship: "",
pc_id: "121292",
consanguineous_marriage: false,
children: [],
problems: [{
id: 0,
name: "cleft lip",
impact: "disease",
remarks: "Need constant monitoring."
},
{
id: 1,
name: "cleft palate",
impact: "carrier",
remarks: "Need constant monitoring."
}]
}
],
problems: [{
id: 0,
name: "cleft lip",
impact: "disease",
remarks: "Need constant monitoring."
},
{
id: 1,
name: "cleft palate",
impact: "carrier",
remarks: "Need constant monitoring."
}]
}
D3族树的代码:
var margin = {top: 140, right: 10, bottom: 140, left: 10};
var height = 600;
var width = 800;
var rectW = 150;
var rectH = 130;
var tree = d3.layout.tree().size([width - 20, height - 60]);
// var root = {};
// get the tree layout
var root = {
patient_name: "Adam Farish",
reference_id: "199210291",
relationship: "",
pc_id: "121292",
consanguineous_marriage: false,
children: [{
patient_name: "Adam Father",
reference_id: "199210291",
relationship: "",
pc_id: "121292",
consanguineous_marriage: false,
children: [],
problems: [{
id: 0,
name: "cleft lip",
impact: "disease",
remarks: "Need constant monitoring."
},
{
id: 1,
name: "cleft palate",
impact: "carrier",
remarks: "Need constant monitoring."
}]
},{
patient_name: "Adam Mother",
reference_id: "199210291",
relationship: "",
pc_id: "121292",
consanguineous_marriage: false,
children: [],
problems: [{
id: 0,
name: "cleft lip",
impact: "disease",
remarks: "Need constant monitoring."
},
{
id: 1,
name: "cleft palate",
impact: "carrier",
remarks: "Need constant monitoring."
}]
}
],
problems: [{
id: 0,
name: "cleft lip",
impact: "disease",
remarks: "Need constant monitoring."
},
{
id: 1,
name: "cleft palate",
impact: "carrier",
remarks: "Need constant monitoring."
}]
};
var nodes = tree(root);
root.parent = root;
root.px = root.x;
root.py = root.y;
root.id = 0;
// make the diagonal link connection
// var diagonal = d3.svg.diagonal();
var diagonal = d3.svg.diagonal()
// .source(function(d) { return {"x":d.source.x, "y":height - d.source.y + 12}; })
.target(function(d) { return {"x":d.target.x, "y": height - d.target.y - 12}; })
.source(function(d) { return {"x":d.source.x, "y":height - d.source.y + 12}; })
.projection(function (d) {
return [d.x + rectW / 2, d.y + rectH /2];
});
var svg = d3.select("#viz").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
var node = svg.selectAll(".node");
var link = svg.selectAll(".link");
function addNode(val,method,rel){
if(method === 'add'){
var n = {id: nodes.length,relationship:rel,problems:[]};
if(val){
console.log("got value");
var p = val;
}else{
console.log("no value");
var p = nodes[Math.random() * nodes.length | 0];
console.log("First node insert",p);
}
if (p.children) p.children.push(n); else p.children = [n];
nodes.push(n);
}else{
if(val){
val.parent.children.forEach(function(entry,i){
if(entry.id === val.id){
val.parent.children.splice(i,1);
}
});
nodes.forEach(function(e,n){
if(e.id === val.id){
nodes.splice(n,1);
}
})
}
}
console.log('ADD NODE RUN!');
}
function removeNode(v){
console.log('remove node',v);
if("parent" in v){
for(var i = v.parent.children.length - 1; i >= 0; i--) {
if(v.parent.children[i].id === v.id) {
v.parent.children.splice(i, 1);
}
}
nodes.forEach(function(entry,i){
if(entry.id === v.id){
nodes.splice(i,1);
}
});
console.log('value',v);
console.log('nodes',nodes);
}
}
var duration = 750
// var timer = setInterval(update, duration);
update();
function update(v,method,rel) {
// if (nodes.length >= 2) return clearInterval(timer);
// if (nodes.length >= 1){
// clearInterval(timer);
// }
console.log("UPDATE RUNS!");
if(method === 'add'){
addNode(v,'add',rel);
}
if(method=='remove'){
addNode(v,'remove');
}
// Add a new node to a random parent.
// Recompute the layout and data join.
node = node.data(tree.nodes(root), function(d) { return d.id; });
link = link.data(tree.links(nodes), function(d) { return d.source.id + "-" + d.target.id; });
console.log(tree.nodes(root));
var nodeEnter = node.enter().append("g");
var foreignObject = nodeEnter.append("foreignObject")
.attr("class", "htmlObject")
.attr("height", rectH)
.attr("width",rectW)
.attr("x", function(d) { return d.parent.px; })
.attr("y", function(d) { return height - d.parent.py + 30; });
var familyTreeNode = foreignObject.append("xhtml:div")
.attr("class","ft-box ft-box-male ft-box-main clearfix");
familyTreeNode.append("xhtml:div")
.attr("class","ft-box-btn clearfix")
.append("xhtml:div")
.attr("class","ft-btn-menu")
.append("xhtml:button")
.attr("class","ft-btn-delete")
.attr("title","Delete")
.attr("dropdown-toggle","")
.attr("aria-haspopup","")
.attr("aria-expanded","false")
.on("click",function(d){
console.log("DELETE THIS NODE!",d);
update(d,"remove");
})
.append("xhtml:em")
.attr("class","fa fa-close");
var dropdownButton = familyTreeNode.append("xhtml:div")
// .attr("class","ft-btn-menu dropdown")
// .attr("dropdown","dropdown");
.attr("class","ft-btn-menu dropdown")
// .attr("dropdown","dropdown");
dropdownButton.append("xhtml:button")
// .attr("class","btn btn-default dropdown-toggle")
.attr("class","ft-btn-add")
.attr("type","button")
.attr("data-toggle","dropdown")
// .attr("dropdown-toggle","")
.attr("title","Add")
.attr("aria-haspopup","true")
.attr("aria-expanded","false")
// .text("Action")
.append("xhtml:em")
.attr("class","fa fa-plus");
var dropdownMenu = dropdownButton
.append("xhtml:ul")
// .attr("class","dropdown-menu");
// .attr("role","menu")
.attr("class","dropdown-menu ft-btn add animated fadeInDown");
dropdownMenu.append("xhtml:div")
.attr("class","arrow");
var listDropdownFather = dropdownMenu.append("xhtml:li")
.append("xhtml:a")
.on("click",function(d){
console.log("Add Father",d);
update(d,'add','father');
});
listDropdownFather.append("xhtml:em")
.attr("class","fa fa-plus-circle");
listDropdownFather.append("xhtml:span")
.attr("class","ft-label-add")
.text("Add Daddy");
var listDropdownMother = dropdownMenu
.append("xhtml:li")
.append("xhtml:a")
.on("click",function(d){
console.log("Add Mommy",d);
if('children' in d){
if(d.children[0].rel !== 'mother' && d.children.length < 2){
console.log('has children but not mother',d.children[0]);
update(d,'add','mother');
}
}
else{
console.log('no children, add Mother');
update(d,'add','mother');
}
});
listDropdownMother.append("xhtml:em")
.attr("class","fa fa-plus-circle");
listDropdownMother.append("xhtml:span")
.attr("class","ft-label-add")
.text("Add Mother");
familyTreeNode.append("xhtml:div")
.attr("class","ft-person-icon clearfix")
.append("xhtml:div")
.attr("class","ft-male-icon")
.attr("title","Info Details")
.on("click", function(d){
console.log("Open Modal Box",d);
$scope.open({d: d});
});
var inputText = familyTreeNode.append("xhtml:input")
.attr("class","ft-person-name ft-name clearfix")
.attr("style","border:none;background: transparent;")
.attr("id","patientname")
.attr("value",function(d){
return d.patient_name;
})
.on("blur",function(d){
// console.log('Blur!',this);
d.patient_name = this.value;
inputText.attr("value",d.patient_name);
console.log(this.value);
});
var footer = familyTreeNode.append("xhtml:div")
.attr("class","ft-footer clearfix");
footer.each(function(d, i) {
var span = d3.select(this)
.selectAll('span')
.data(d.problems);
span.enter()
.append("xhtml:span")
.attr("class",function(d,i){
var myStr = d.name.toLowerCase();
var matches = myStr.match(/\b(\w)/g);
// console.log('APPEND',matches);
return "ft-disease ft-"+matches.join('');
})
.text(function(prob){
console.log('APPEND',prob);
var myStr = prob.name.toUpperCase();
var matches = myStr.match(/\b(\w)/g);
return matches.join('');
})
});
node.exit().remove();
link.exit().remove();
link.enter().insert("path", "g")
.attr("class", "link")
.attr("x", rectW / 2)
.attr("y", rectH / 2)
.attr("d", function (d) {
var o = {
x: d.source.px,
y: height - d.source.py
};
return diagonal({
source: o,
target: o
});
});
link.exit().transition()
.duration(duration)
.attr("d", function (d) {
var o = {
x: d.source.x,
y: height - d.source.y
};
return diagonal({
source: o,
target: o
});
}).remove();
var t = svg.transition()
.duration(duration);
t.selectAll(".link")
.attr("d", diagonal);
t.selectAll(".node")
.attr("x", function(d) { return d.px = d.x; })
.attr("y", function(d) { return d.py = height - d.y; });
t.selectAll(".htmlObject")
.attr("x", function(d) { return d.px = d.x; })
.attr("y", function(d) { return d.py = height - d.y; });
}
答案 0 :(得分:0)
好吧,似乎我没有为每个成员启动id,因为生成节点的重要部分高度依赖于id(你可以将它改为任何独特的东西)。
node = node.data(tree.nodes(root), function(d) { return d.id; });
link = link.data(tree.links(nodes), function(d) { return d.source.id + "-" + d.target.id; });
加载可视化文件所需的初始数据必须具有以下内容:
{
id:2,
patient_name: "Adam Father",
reference_id: "199210291",
relationship: "",
pc_id: "121292",
consanguineous_marriage: false,
children: [],
problems: [{
id: 0,
name: "cleft lip",
impact: "disease",
remarks: "Need constant monitoring."
},
{
id: 1,
name: "cleft palate",
impact: "carrier",
remarks: "Need constant monitoring."
}]
},{
id:3,
patient_name: "Adam Mother",
reference_id: "199210291",
relationship: "",
pc_id: "121292",
consanguineous_marriage: false,
children: [],
problems: [{
id: 0,
name: "cleft lip",
impact: "disease",
remarks: "Need constant monitoring."
},
{
id: 1,
name: "cleft palate",
impact: "carrier",
remarks: "Need constant monitoring."
}]
}
工作jsfiddle