Search code examples
javascriptchartstreeexpandgojs

GoJS expanding and collapsing tree nodes


I'm a new in Gojs, and this question is about the node expandind control in tree model.

I just wonder can I control the root node in different way to the other node in tree model.

Take the Gojs official code for example:

  $(go.Node, "Horizontal",
    $(go.Panel, "Auto",
      $(go.Shape, "Ellipse", { fill: null }),
      $(go.TextBlock,
        new go.Binding("text", "key"))
    ),
    $("TreeExpanderButton")
  );

diagram.layout = $(go.TreeLayout);

var nodeDataArray = [
  { key: "Alpha" }, { key: "Beta" }, { key: "Gamma" }, { key: "Delta" },
  { key: "Epsilon" }, { key: "Zeta" }, { key: "Eta" }, { key: "Theta" }
];
var linkDataArray = [
  { from: "Alpha", to: "Beta" },
  { from: "Beta", to: "Gamma" },
  { from: "Beta", to: "Delta" },
  { from: "Alpha", to: "Epsilon" },
  { from: "Epsilon", to: "Zeta" },
  { from: "Epsilon", to: "Eta" },
  { from: "Epsilon", to: "Theta" }
];
diagram.model = new go.GraphLinksModel(nodeDataArray, linkDataArray);

and we will get the tree like1

How can I remove the treeExpanderButton on the root node only, and keep the root node be expanded ?


Solution

  • The problem is that the predefined "TreeExpanderButton" automatically becomes visible or invisible only based on whether there are any links to any child nodes.

    So one solution is to copy the definition of "TreeExpanderButton" from extensions/Buttons.js and remove the Binding of the button's GraphObject.visible property that depends on the Node.isTreeLeaf property. That will allow us to implement our own scheme for determining the Node.visible property, which in this case can be done by implementing Node.linkConnected and Node.linkDisconnected event handlers:

    <!DOCTYPE html>
    <html>
    <head>
      <title>Minimal GoJS Sample</title>
      <!-- Copyright 1998-2022 by Northwoods Software Corporation. -->
    </head>
    <body>
      <div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:600px"></div>
    
      <script src="https://unpkg.com/gojs"></script>
      <script id="code">
    // copied from extensions/Buttons.js so that we can remove the predefined
    // binding on the "visible" property.
    go.GraphObject.defineBuilder('TreeExpanderButton', function (args) {
      var button = /** @type {Panel} */ (
        go.GraphObject.make('Button',
          { // set these values for the isTreeExpanded binding conversion
            '_treeExpandedFigure': 'MinusLine',
            '_treeCollapsedFigure': 'PlusLine'
          },
          go.GraphObject.make(go.Shape,  // the icon
            {
              name: 'ButtonIcon',
              figure: 'MinusLine',  // default value for isTreeExpanded is true
              stroke: '#424242',
              strokeWidth: 2,
              desiredSize: new go.Size(8, 8)
            },
            // bind the Shape.figure to the Node.isTreeExpanded value using this converter:
            new go.Binding('figure', 'isTreeExpanded',
              function (exp, shape) {
                var but = shape.panel;
                return exp ? but['_treeExpandedFigure'] : but['_treeCollapsedFigure'];
              }
            ).ofObject()
          ),
          // assume initially not visible because there are no links coming out
          { visible: false }
          // NOTE: removed the Binding on "visible" depending on Node.isTreeLeaf
        )
      );
    
      // tree expand/collapse behavior
      button.click = function (e, btn) {
        var node = btn.part;
        if (node instanceof go.Adornment) node = node.adornedPart;
        if (!(node instanceof go.Node)) return;
        var diagram = node.diagram;
        if (diagram === null) return;
        var cmd = diagram.commandHandler;
        if (node.isTreeExpanded) {
          if (!cmd.canCollapseTree(node)) return;
        } else {
          if (!cmd.canExpandTree(node)) return;
        }
        e.handled = true;
        if (node.isTreeExpanded) {
          cmd.collapseTree(node);
        } else {
          cmd.expandTree(node);
        }
      };
    
      return button;
    });
    
    const $ = go.GraphObject.make;
    
    const myDiagram =
      $(go.Diagram, "myDiagramDiv",
        {
          layout: $(go.TreeLayout),
          "undoManager.isEnabled": true
        });
    
    myDiagram.nodeTemplate =
      $(go.Node, "Horizontal",
        $(go.Panel, "Auto",
          $(go.Shape, { fill: "white" },
            new go.Binding("fill", "color")),
          $(go.TextBlock, { margin: 8 },
            new go.Binding("text"))
        ),
        $("TreeExpanderButton", { name: "BUTTON" }),
        {
          linkConnected: (node, link, port) => node.findObject("BUTTON").visible = node.findTreeChildrenLinks().count > 0 && node.findTreeParentLink() !== null,
          linkDisconnected: (node, link, port) => node.findObject("BUTTON").visible = node.findTreeChildrenLinks().count > 0 && node.findTreeParentLink() !== null
        }
      );
    
    myDiagram.model = new go.GraphLinksModel(
    [
      { key: 1, text: "Alpha", color: "lightblue" },
      { key: 2, text: "Beta", color: "orange" },
      { key: 3, text: "Gamma", color: "lightgreen" },
      { key: 4, text: "Delta", color: "pink" },
      { key: 5, text: "Epsilon", color: "yellow" }
    ],
    [
      { from: 1, to: 2 },
      { from: 1, to: 3 },
      { from: 3, to: 4 },
      { from: 3, to: 5 }
    ]);
      </script>
    </body>
    </html>