Trees and TreeLayout

There is no limit to the kinds of graphs that you can build in GoJS. But the most common kind of graph forms a "tree". A tree is a graph where each node may have at most one "tree parent" and at most one link connecting to that parent node, and where there are no cycles within the graph.

Because trees occur so frequently in diagrams, there is also a tree layout that offers many customizations specifically for trees.

Manual layout of a tree structure

You can of course position the nodes manually, either by hand or programmatically. In this first example, the node locations are stored in the node data, and there is a Binding of Part.location to the node data property.


  diagram.nodeTemplate =
    $(go.Node, "Auto",
      new go.Binding("location", "loc", go.Point.parse),
      $(go.Shape, "Ellipse", { fill: "white" }),
      $(go.TextBlock,
        new go.Binding("text", "key"))
    );

  diagram.linkTemplate =
    $(go.Link,
      { routing: go.Routing.Orthogonal, corner: 5 },
      $(go.Shape));

  var nodeDataArray = [
    { key: "Alpha", loc: "0 60" },
    { key: "Beta", loc: "100 15" },
    { key: "Gamma", loc: "200 0" },
    { key: "Delta", loc: "200 30" },
    { key: "Epsilon", loc: "100 90" },
    { key: "Zeta", loc: "200 60" },
    { key: "Eta", loc: "200 90" },
    { key: "Theta", loc: "200 120" }
  ];
  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);

You can also get the same results by using a TreeModel.


  diagram.nodeTemplate =
    $(go.Node, "Auto",
      new go.Binding("location", "loc", go.Point.parse),
      $(go.Shape, "Ellipse", { fill: "white" }),
      $(go.TextBlock,
        new go.Binding("text", "key"))
    );

  diagram.linkTemplate =
    $(go.Link,
      { routing: go.Routing.Orthogonal, corner: 5 },
      $(go.Shape));

  var nodeDataArray = [
    { key: "Alpha", loc: "0 60" },
    { key: "Beta", loc: "100 15", parent: "Alpha" },
    { key: "Gamma", loc: "200 0", parent: "Beta" },
    { key: "Delta", loc: "200 30", parent: "Beta" },
    { key: "Epsilon", loc: "100 90", parent: "Alpha" },
    { key: "Zeta", loc: "200 60", parent: "Epsilon" },
    { key: "Eta", loc: "200 90", parent: "Epsilon" },
    { key: "Theta", loc: "200 120", parent: "Epsilon" }
  ];
  diagram.model = new go.TreeModel(nodeDataArray);

Automatic TreeLayout

It is most common to use TreeLayout for laying out trees. Just assign Diagram.layout to a new instance of TreeLayout. This example also defines the setupTree function that is used in later examples on this page.


function setupTree(diagram) {
  diagram.nodeTemplate =
    $(go.Node, "Auto",
      $(go.Shape, "Ellipse", { fill: "white" }),
      $(go.TextBlock,
        new go.Binding("text", "key"))
    );

  diagram.linkTemplate =
    $(go.Link,
      { routing: go.Routing.Orthogonal, corner: 5 },
      $(go.Shape));

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

  setupTree(diagram);
  // automatic tree layout
  diagram.layout = $(go.TreeLayout);

Common TreeLayout properties

The TreeLayout.angle property controls the general direction of tree growth. This must be zero (towards the right), 90 (downward), 180 (leftward), or 270 (upward).


  setupTree(diagram);
  diagram.layout = $(go.TreeLayout, { angle: 90 });

The setupTree function was defined above.

The TreeLayout.alignment property controls how the parent node is positioned relative to its children. This must be one of the Alignment... constants defined on TreeLayout.


  setupTree(diagram);
  diagram.layout = $(go.TreeLayout, { angle: 90, alignment: go.TreeAlignment.Start });

For tree layouts, all of the nodes are placed into "layers" according to the length of the chain of links from the root node. These layers are not to be confused with Diagram Layers, which control the Z-ordering of the nodes. The TreeLayout.layerSpacing property controls how close the layers are to each other. The TreeLayout.nodeSpacing property controls how close nodes are to each other in the same layer.


  setupTree(diagram);
  diagram.layout = $(go.TreeLayout, { layerSpacing: 20, nodeSpacing: 0 });

The children of each node can be sorted. By default the TreeLayout.comparer function compares the Part.text property. So if that property is data bound by the node template, and if you set the TreeLayout.sorting property to sort in either ascending or descending order, each parent node will have all of its children sorted in that order by their text strings. (In this example that means alphabetical ordering of the English names of the letters of the Greek alphabet.)


  setupTree(diagram);
  diagram.nodeTemplate =
    $(go.Node, "Auto",
      new go.Binding("text", "key"),  // bind Part.text to support sorting
      $(go.Shape, "Ellipse", { fill: "lightblue" }),
      $(go.TextBlock,
        new go.Binding("text", "key"))
    );
  diagram.layout = $(go.TreeLayout, { sorting: go.TreeSorting.Ascending });

But you can provide your own function for ordering the children, such as:


  new go.Diagram(. . .,
    {
      layout:
          $(go.TreeLayout,
            {
              sorting: go.TreeSorting.Ascending,
              comparer: (a, b) => {
                  // A and B are TreeVertexes
                  const av = a.node.data.index;
                  const bv = b.node.data.index;
                  if (av < bv) return -1;
                  if (av > bv) return 1;
                  return 0;
                },
              . . .
            })
      . . .
    })