D3.js树-使用搜索进行分页 [英] d3.js Tree - Paging with search

查看:32
本文介绍了D3.js树-使用搜索进行分页的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个previous question,它有一个分页子节点的解决方案。

此操作运行良好,但已导致用于查找d.type == 'learning_event'的节点的Select2列表出现问题。

Select2显示节点,但树中分页的节点不显示在Select2中。

相反,显示分页节点的名称‘...’,而不是分页节点的名称。

参见fiddle

小提琴中的代码:

    //===============================================
    function learningEventCollection(d) {
        if (d.children)
            d.children.forEach(learningEventCollection);
        else if (d._children)
            d._children.forEach(learningEventCollection);
        if (!d.children && d.type == 'learning_event') learningEventData.push(d.name);
    }

    //===============================================
    function searchTree(d) {
        if (d.children)
            d.children.forEach(searchTree);
        else if (d._children)
            d._children.forEach(searchTree);
        var searchFieldValue = eval(searchField);
        if (searchFieldValue && searchFieldValue.toLowerCase().match(searchText.toLowerCase())) {
            // Walk parent chain
            var ancestors = [];
            var parent = d;
            while (typeof(parent) !== "undefined") {
                ancestors.push(parent);
                //console.log(parent);
                parent.class = "found";
                parent = parent.parent;
            }
            //console.log(ancestors);
        }
    }

    //===============================================
    function clearAll(d) {
        d.class = "";
        if (d.children)
            d.children.forEach(clearAll);
        else if (d._children)
            d._children.forEach(clearAll);
    }
    //===============================================
    function collapse(d) {

        if (d.children) {
            d._children = d.children;
            //set the parent object in all the children
            d._children.forEach(function(d1) {
                d1.parent = d;
                collapse(d1);
            });
            d.children = null;
        }
    }
    //===============================================
    function collapseAllNotFound(d) {
        if (d.children) {
            if (d.class !== "found") {
                d._children = d.children;
                d._children.forEach(collapseAllNotFound);
                d.children = null;
            } else
                d.children.forEach(collapseAllNotFound);
        }
    }
    //===============================================
    function expandAll(d) {
        if (d._children) {
            d.children = d._children;
            d.children.forEach(expandAll);
            d._children = null;
        } else if (d.children)
            d.children.forEach(expandAll);
    }

    //===============================================
    // Toggle children on click.
    function toggle(d) {
        if (d.children) {
            d._children = d.children;
            d.children = null;
        } else {
            d.children = d._children;
            d._children = null;
        }
        clearAll(root);
        update(d);
        $("#search").select2("val", "");
    }

    //===============================================
    $("#search").on("select2-selecting", function(e) {
        clearAll(root);
        expandAll(root);
        update(root);
        searchField = "d.name";
        searchText = e.object.text;
        searchTree(root);
        root.children.forEach(collapseAllNotFound);
        update(root);
    })
    
        var colourScale = d3.scale.ordinal()
        .domain(["Program", "ProgramGroup1", "ProgramGroup2"])
        .range(["#abacab", "#b67a4e", "#5a6fbb"])
    

var margin = {
    top: 20,
    right: 120,
    bottom: 20,
    left: 120
  },
  width = 2000 - margin.right - margin.left,
  height = 500 - margin.top - margin.bottom;

var i = 0,
  duration = 750,
  root;

var tree = d3.layout.tree()
  .size([height, width]);

var diagonal = d3.svg.diagonal()
  .projection(function(d) {
    return [d.y, d.x];
  });

var svg = d3.select("body").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 + ")");

root = treeData;
root.x0 = height / 2;
root.y0 = 0;

function pageNodes(d, options) {
  if (d.children) {
    d.children.forEach(c => pageNodes(c, options));
    if (options.page && options.page(d) && d.children.length > options.maxNode) {
      d.pages = {}
      const count = options.maxNode - 2;
      const l = Math.ceil(d.children.length / count);
      for (let i = 0; i < l; i++) {
        const startRange = i * count;
        const endRange = i * count + count;
        let pageNumber = i == 0 ? l - 1 : i - 1;
        d.pages[i] = d.children.slice(startRange, endRange);
        d.pages[i].unshift({
          ...d.pages[i][0],
          data: {
            name: options.getLabel ? options.getLabel(pageNumber) : "..."
          },
          pageNumber,
          name: "..."
        })

        // console.log(i, d.pages[i]);
        pageNumber = i != (l - 1) ? i + 1 : 0;
        d.pages[i].push({
          ...d.pages[i][0],
          data: {
            name: options.getLabel ? options.getLabel(pageNumber) : "..."
          },
          pageNumber,
          name: "..."
        });
      }
      d.children = d.pages[0];
      console.log(d.pages)
    }
  }
}
root.children.forEach(c => pageNodes(c, {
  maxNode: 8,
  page: function(d) {
    return d.type == "unit_group";
  }
}));


 learningEventData = [];
        learningEventCollection(root);
        learningEventDataObject = [];
        learningEventData.sort(function(a, b) {
                if (a > b) return 1; // sort
                if (a < b) return -1;
                return 0;
            })
            .filter(function(item, i, ar) {
                return ar.indexOf(item) === i;
            }) // remove duplicate items
            .filter(function(item, i, ar) {
                learningEventDataObject.push({
                    "id": i,
                    "text": item
                });
            });
        
        $("#search").select2({
            placeholder: "Select a Learning Event...",
            data: learningEventDataObject,
            containerCssClass: "search"
        });
        
        

function collapse(d) {
  if (d.children) {
    d._children = d.children;
    d._children.forEach(collapse);
    d.children = null;
  }
}

root.children.forEach(collapse);
update(root);

//svg.style("height", "500px");

function update(source) {

  // Compute the new tree layout.
  var nodes = tree.nodes(root).reverse(),
    links = tree.links(nodes);

  // Normalize for fixed-depth.
  nodes.forEach(function(d) {
    d.y = d.depth * 180;
  });

  // Update the nodes…
  var node = svg.selectAll("g.node")
    .data(nodes, function(d) {
      return d.id || (d.id = ++i);
    });

  // Enter any new nodes at the parent's previous position.
  var nodeEnter = node.enter().append("g")
    .attr("class", "node")
    .attr("transform", function(d) {
      return "translate(" + source.y0 + "," + source.x0 + ")";
    })
    .on("click", click);

  nodeEnter.append("circle")
    .attr("r", 1e-6)
    .attr('stroke', function(d) {
      return d.color ? d.color : 'blue';
    })
    .style("fill", function(d) {
      return d._children ? "#ccc" : "#fff";
    });

  nodeEnter.append("text")
    .attr("x", function(d) {
      return d.children || d._children ? -13 : 13;
    })
    .attr("dy", ".35em")
    .attr("text-anchor", function(d) {
      return d.children || d._children ? "end" : "start";
    })
    .text(function(d) {
      return d.name;
    })
    .style("fill-opacity", 1e-6);

  // Transition nodes to their new position.
  var nodeUpdate = node.transition()
    .duration(duration)
    .attr("transform", function(d) {
      return "translate(" + d.y + "," + d.x + ")";
    });

   nodeUpdate.select("circle")
            .attr("r", 6.5)
            .attr("fill-opacity", "0.7")
            .attr("stroke-opacity", "1")
            .style("fill", function(d) {
            if (d.class !== "found") {
                if(d.type == "learning_event") return "#6eb7e3";
                if(d.type == "assessment") return "#4ecc44";
            }
                if (d.class === "found") {
                    return "#ff4136"; //red
                } else {
                    return (typeof d._children !== 'undefined') ? (colourScale(findParent(d))) : '#FFF';

                }
            })
            .style("stroke", function(d) {
             if (d.class !== "found") {
                if(d.type == "learning_event") return "#6eb7e3";
                if(d.type == "assessment") return "#4ecc44";
                if(d.name.match(/Learning Events/)) return "#6eb7e3";
                if(d.name.match(/Assessments/)) return "#4ecc44";
             }
                if (d.class === "found") {
                    return "#ff4136"; //red
                } else {
                    return colourScale(findParent(d));
                }
            });
    
    

  nodeUpdate.select("text")
    .style("fill-opacity", 1);

  // Transition exiting nodes to the parent's new position.
  var nodeExit = node.exit().transition()
    .duration(duration)
    .attr("transform", function(d) {
      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 = svg.selectAll("path.link")
    .data(links, function(d) {
      return d.target.id;
    });

  // Enter any new links at the parent's previous position.
  link.enter().insert("path", "g")
    .attr("class", "link")
    .attr("d", function(d) {
      var o = {
        x: source.x0,
        y: source.y0
      };
      return diagonal({
        source: o,
        target: o
      });
    });

  // Transition links to their new position.
  link.transition()
    .duration(duration)
    .attr("d", diagonal);

  // Transition exiting nodes to the parent's new position.
  link.exit().transition()
    .duration(duration)
    .attr("d", function(d) {
      var o = {
        x: source.x,
        y: source.y
      };
      return diagonal({
        source: o,
        target: o
      });
    })
    .remove();

  // Stash the old positions for transition.
  nodes.forEach(function(d) {
    d.x0 = d.x;
    d.y0 = d.y;
  });
}

    function findParent(datum) {
        if (datum.depth < 2) {
            return datum.name
        } else {
            return findParent(datum.parent)
        }
    }

    function findParentLinks(datum) {
        if (datum.target.depth < 2) {
            return datum.target.name
        } else {
            return findParent(datum.target.parent)
        }
    }

// Toggle children on click.
function click(d) {
  if (d.hasOwnProperty('pageNumber')) {
    d.parent.children = d.parent.pages[d.pageNumber];
  } else if (d.children) {
    d._children = d.children;
    d.children = null;
  } else {
    d.children = d._children;
    d._children = null;
  }
  update(d);
}

推荐答案

  • 问题是type被分配为同一级别中的其他节点,因此我将type更改为page-toggle-node
  • 对于Select2,我已在选项组中为所有节点修改了它,并相应地调整了搜索功能
  • 您使用match比较文本时遇到了一个问题,当LE1LE15进行比较时,它将返回True,因为LE15包含LE1
var treeData = {"name":"Program","column_to_sort_by":null,"type":"program","children":[{"name":"ProgramGroup1","column_to_sort_by":null,"type":"program_group","children":[{"name":"POGroup1","column_to_sort_by":null,"type":"1program_outcome_group","children":[{"name":"PO1","column_to_sort_by":null,"type":"program_outcome","children":[{"name":"Unit1","column_to_sort_by":"Unit1","children":[{"name":"UG1-LE","column_to_sort_by":null,"type":"unit_group","children":[{"name":"LE1","column_to_sort_by":"LE1","type":"learning_event"},{"name":"LE10","column_to_sort_by":"LE10","type":"learning_event"},{"name":"LE11","column_to_sort_by":"LE11","type":"learning_event"},{"name":"LE12","column_to_sort_by":"LE12","type":"learning_event"},{"name":"LE13","column_to_sort_by":"LE13","type":"learning_event"},{"name":"LE14","column_to_sort_by":"LE14","type":"learning_event"},{"name":"LE15","column_to_sort_by":"LE15","type":"learning_event"},{"name":"LE2","column_to_sort_by":"LE2","type":"learning_event"},{"name":"LE4","column_to_sort_by":"LE4","type":"learning_event"},{"name":"LE5","column_to_sort_by":"LE5","type":"learning_event"},{"name":"LE6","column_to_sort_by":"LE6","type":"learning_event"},{"name":"LE7","column_to_sort_by":"LE7","type":"learning_event"},{"name":"LE8","column_to_sort_by":"LE8","type":"learning_event"},{"name":"LE9","column_to_sort_by":"LE9","type":"learning_event"}]},{"name":"UG1-Assessments","column_to_sort_by":null,"type":"unit_group","children":[{"name":"ASST1","column_to_sort_by":"ASST1","type":"assessment"},{"name":"ASST10","column_to_sort_by":"ASST10","type":"assessment"},{"name":"ASST11","column_to_sort_by":"ASST11","type":"assessment"},{"name":"ASST13","column_to_sort_by":"ASST13","type":"assessment"},{"name":"ASST14","column_to_sort_by":"ASST14","type":"assessment"},{"name":"ASST15","column_to_sort_by":"ASST15","type":"assessment"},{"name":"ASST2","column_to_sort_by":"ASST2","type":"assessment"},{"name":"ASST3","column_to_sort_by":"ASST3","type":"assessment"},{"name":"ASST4","column_to_sort_by":"ASST4","type":"assessment"},{"name":"ASST5","column_to_sort_by":"ASST5","type":"assessment"},{"name":"ASST6","column_to_sort_by":"ASST6","type":"assessment"},{"name":"ASST7","column_to_sort_by":"ASST7","type":"assessment"},{"name":"ASST8","column_to_sort_by":"ASST8","type":"assessment"},{"name":"ASST9","column_to_sort_by":"ASST9","type":"assessment"}]}],"type":"unit"}]},{"name":"PO2","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO3","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO4","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO5","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO6","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO7","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO8","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO9","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO10","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO11","column_to_sort_by":null,"type":"program_outcome"}]},{"name":"POGroup2","column_to_sort_by":null,"type":"1program_outcome_group"}]},{"name":"ProgramGroup2","column_to_sort_by":null,"type":"program_group"}]};

//===============================================
function searchTree(d) {
  if (d.pages) {
    const pageKeys = Object.keys(d.pages)
    Object.keys(d.pages).forEach(page => {
      let currentNodes = d.pages[page];
      currentNodes.forEach((node, idx) => {
        if (node.name.toLowerCase() == searchText.toLowerCase()) {
          currentNodes[idx]['parent'] = d.children[0].parent;
          d.children = currentNodes;
        }
      })
    });
  }

  if (d.children)
    d.children.forEach(searchTree);
  else if (d._children)
    d._children.forEach(searchTree);
  var searchFieldValue = eval(searchField);
  if (searchFieldValue && searchFieldValue.toLowerCase().match(searchText.toLowerCase())) {
    // Walk parent chain
    var ancestors = [];
    var parent = d;
    while (typeof(parent) !== "undefined") {
      ancestors.push(parent);
      //console.https://jsfiddle.net/qyn5fd0t/#runlog(parent);
      parent.class = "found";
      parent = parent.parent;
    }
    // console.log(ancestors);
  }
}

//===============================================
function clearAll(d) {
  d.class = "";
  if (d.children)
    d.children.forEach(clearAll);
  else if (d._children)
    d._children.forEach(clearAll);
}
//===============================================
function collapse(d) {

  if (d.children) {
    d._children = d.children;
    //set the parent object in all the children
    d._children.forEach(function(d1) {
      d1.parent = d;
      collapse(d1);
    });
    d.children = null;
  }
}
//===============================================
function collapseAllNotFound(d) {
  if (d.children) {
    if (d.class !== "found") {
      d._children = d.children;
      d._children.forEach(collapseAllNotFound);
      d.children = null;
    } else
      d.children.forEach(collapseAllNotFound);
  }
}
//===============================================
function expandAll(d) {
  if (d._children) {
    d.children = d._children;
    d.children.forEach(expandAll);
    d._children = null;
  } else if (d.children)
    d.children.forEach(expandAll);
}

//===============================================
// Toggle children on click.
function toggle(d) {
  if (d.children) {
    d._children = d.children;
    d.children = null;
  } else {
    d.children = d._children;
    d._children = null;
  }
  clearAll(root);
  update(d);
  $("#search").select2("val", "");
}

//===============================================
$("#search").on("select2-selecting", function(e) {
  clearAll(root);
  expandAll(root);
  update(root);
  searchField = "d.name";
  searchText = e.object.text;
  searchTree(root);
  root.children.forEach(collapseAllNotFound);
  update(root);
})

var colourScale = d3.scale.ordinal()
  .domain(["Program", "ProgramGroup1", "ProgramGroup2"])
  .range(["#abacab", "#b67a4e", "#5a6fbb"])

var margin = {
    top: 20,
    right: 120,
    bottom: 20,
    left: 120
  },
  width = 2000 - margin.right - margin.left,
  height = 500 - margin.top - margin.bottom;

var i = 0,
  duration = 750,
  root;

var tree = d3.layout.tree()
  .size([height, width]);

var diagonal = d3.svg.diagonal()
  .projection(function(d) {
    return [d.y, d.x];
  });

var svg = d3.select("body").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 + ")");

root = treeData;
root.x0 = height / 2;
root.y0 = 0;

function pageNodes(d, options) {
  if (d.children) {
    d.children.forEach(c => pageNodes(c, options));
    if (options.isPageable && options.isPageable(d) && d.children.length > options.maxNode) {
      d.pages = {}
      const count = options.maxNode - 2;
      const l = Math.ceil(d.children.length / count);
      for (let i = 0; i < l; i++) {
        const startRange = i * count;
        const endRange = i * count + count;
        let pageNumber = i == 0 ? l - 1 : i - 1;
        d.pages[i] = d.children.slice(startRange, endRange);
        options.page && options.page(d.pages[i]);
        d.pages[i].unshift({
          ...d.pages[i][0],
          data: {
            name: options.getLabel ? options.getLabel(pageNumber) : "..."
          },
          pageNumber,
          name: "...",
          type: "page-toggle-node"
        })

        // console.log(i, d.pages[i]);
        pageNumber = i != (l - 1) ? i + 1 : 0;
        d.pages[i].push({
          ...d.pages[i][0],
          data: {
            name: options.getLabel ? options.getLabel(pageNumber) : "..."
          },
          pageNumber,
          name: "...",
          type: "page-toggle-node"
        });
      }
      d.children = d.pages[0];
    }
  }
}

//===============================================
const groupLearningOptions = [];
let groupLearningOptionsCounter = 0;
root.children.forEach(c => pageNodes(c, {
  maxNode: 8,
  isPageable: function(d) {
    return d.type == "unit_group";
  },
  page: function(nodes) {
    if (nodes[0].type == "learning_event") {
      groupLearningOptions.push({
        text: `Group ${groupLearningOptions.length + 1}`,
        children: nodes.map(node => ({
          id: groupLearningOptionsCounter++,
          text: node.name
        }))
        .sort((a, b) => (a.text > b.text) ? 1 : -1)
      })
    }
  }
}));
// console.log(groupLearningOptions);

$("#search").select2({
  placeholder: "Select a Learning Event...",
  data: {
    results: groupLearningOptions
  },
  containerCssClass: "search"
});


function collapse(d) {
  if (d.children) {
    d._children = d.children;
    d._children.forEach(collapse);
    d.children = null;
  }
}

root.children.forEach(collapse);
update(root);

//svg.style("height", "500px");

function update(source) {

  // Compute the new tree layout.
  var nodes = tree.nodes(root).reverse(),
    links = tree.links(nodes);

  // Normalize for fixed-depth.
  nodes.forEach(function(d) {
    d.y = d.depth * 180;
  });

  // Update the nodes…
  var node = svg.selectAll("g.node")
    .data(nodes, function(d) {
      return d.id || (d.id = ++i);
    });

  // Enter any new nodes at the parent's previous position.
  var nodeEnter = node.enter().append("g")
    .attr("class", "node")
    .attr("transform", function(d) {
      return "translate(" + source.y0 + "," + source.x0 + ")";
    })
    .on("click", click);

  nodeEnter.append("circle")
    .attr("r", 1e-6)
    .attr('stroke', function(d) {
      return d.color ? d.color : 'blue';
    })
    .style("fill", function(d) {
      return d._children ? "#ccc" : "#fff";
    });

  nodeEnter.append("text")
    .attr("x", function(d) {
      return d.children || d._children ? -13 : 13;
    })
    .attr("dy", ".35em")
    .attr("text-anchor", function(d) {
      return d.children || d._children ? "end" : "start";
    })
    .text(function(d) {
      return d.name;
    })
    .style("fill-opacity", 1e-6);

  // Transition nodes to their new position.
  var nodeUpdate = node.transition()
    .duration(duration)
    .attr("transform", function(d) {
      return "translate(" + d.y + "," + d.x + ")";
    });

  nodeUpdate.select("circle")
    .attr("r", 6.5)
    .attr("fill-opacity", "0.7")
    .attr("stroke-opacity", "1")
    .style("fill", function(d) {
      if (d.class !== "found") {
        if (d.type == "learning_event") return "#6eb7e3";
        if (d.type == "assessment") return "#4ecc44";
      }
      if (d.class === "found") {
        return "#ff4136"; //red
      } else {
        return (typeof d._children !== 'undefined') ? (colourScale(findParent(d))) : '#FFF';

      }
    })
    .style("stroke", function(d) {
      if (d.class !== "found") {
        if (d.type == "learning_event") return "#6eb7e3";
        if (d.type == "assessment") return "#4ecc44";
        if (d.name.match(/Learning Events/)) return "#6eb7e3";
        if (d.name.match(/Assessments/)) return "#4ecc44";
      }
      if (d.class === "found") {
        return "#ff4136"; //red
      } else {
        return colourScale(findParent(d));
      }
    });



  nodeUpdate.select("text")
    .style("fill-opacity", 1);

  // Transition exiting nodes to the parent's new position.
  var nodeExit = node.exit().transition()
    .duration(duration)
    .attr("transform", function(d) {
      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 = svg.selectAll("path.link")
    .data(links, function(d) {
      return d.target.id;
    });

  // Enter any new links at the parent's previous position.
  link.enter().insert("path", "g")
    .attr("class", "link")
    .attr("d", function(d) {
      var o = {
        x: source.x0,
        y: source.y0
      };
      return diagonal({
        source: o,
        target: o
      });
    });

  // Transition links to their new position.
  link.transition()
    .duration(duration)
    .attr("d", diagonal);

  // Transition exiting nodes to the parent's new position.
  link.exit().transition()
    .duration(duration)
    .attr("d", function(d) {
      var o = {
        x: source.x,
        y: source.y
      };
      return diagonal({
        source: o,
        target: o
      });
    })
    .remove();

  // Stash the old positions for transition.
  nodes.forEach(function(d) {
    d.x0 = d.x;
    d.y0 = d.y;
  });
}

function findParent(datum) {
  if (datum.depth < 2) {
    return datum.name
  } else {
    return findParent(datum.parent)
  }
}

function findParentLinks(datum) {
  if (datum.target.depth < 2) {
    return datum.target.name
  } else {
    return findParent(datum.target.parent)
  }
}

// Toggle children on click.
function click(d) {
  if (d.hasOwnProperty('pageNumber')) {
    d.parent.children = d.parent.pages[d.pageNumber];
  } else if (d.children) {
    d._children = d.children;
    d.children = null;
  } else {
    d.children = d._children;
    d._children = null;
  }
  update(d);
}

在此工作example

这篇关于D3.js树-使用搜索进行分页的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆