Positioning Nodes in GoJs

Hi Team,

I have created nodes and mapped them as shown in the below code.

import { h, Fragment } from "preact";
import { useEffect, useState } from "preact/hooks";

declare global {
  interface Window {
    go: any;
  }
}

export function GoJsEval() {
  useEffect(() => {
    init();
  }, []); // <-- empty array means 'run once'

  function init() {
    const go = window.go;
    var $ = go.GraphObject.make; // for conciseness in defining templates
    var ml8 = new go.Margin(0, 0, 0, 8);
    var myDiagram = $(
      go.Diagram,
      "myDiagramDiv", // create a Diagram for the DIV HTML element
      {
        "animationManager.isEnabled": false,
        // allow double-click in background to create a new node
        "clickCreatingTool.archetypeNodeData": { text: "Node", color: "white" },
        // allow Ctrl-G to call groupSelection()
        "commandHandler.archetypeGroupData": {
          text: "Group",
          isGroup: true,
          color: "blue",
        },
        // enable undo & redo
        "undoManager.isEnabled": true,
        initialDocumentSpot: go.Spot.Top,
        initialViewportSpot: go.Spot.Top,
        // OR: Scroll to show a particular node, once the layout has determined where that node is
        // "InitialLayoutCompleted": e => {
        //  var node = e.diagram.findNodeForKey(28);
        //  if (node !== null) e.diagram.commandHandler.scrollToPart(node);
        // },
        layout: $(
          go.TreeLayout, // use a TreeLayout to position all of the nodes
          {
            isOngoing: false, // don't relayout when expanding/collapsing panels
            treeStyle: go.TreeLayout.StyleLastParents,
            // properties for most of the tree:
            angle: 90,
            layerSpacing: 80,
            // properties for the "last parents":
            alternateAngle: 0,
            alternateAlignment: go.TreeLayout.AlignmentStart,
            alternateNodeIndent: 15,
            alternateNodeIndentPastParent: 1,
            alternateNodeSpacing: 15,
            alternateLayerSpacing: 40,
            alternateLayerSpacingParentOverlap: 1,
            alternatePortSpot: new go.Spot(0.001, 1, 20, 0),
            alternateChildPortSpot: go.Spot.Left,
          }
        ),
        // automatically show the state of the diagram's model on the page
        //   "ModelChanged": e => {
        //     if (e.isTransactionFinished) {
        //       document.getElementById("savedModel").textContent = myDiagram.model.toJson();
        //     }
        //   }
      }
    );
    function textStyle(field) {
      return [
        {
          font: "12px Roboto, sans-serif",
          stroke: "rgba(0, 0, 0, .60)",
          visible: false, // only show textblocks when there is corresponding data for them
        },
        new go.Binding("visible", field, (val) => val !== undefined),
      ];
    }

    var roundedRectangleParams = {
      parameter1: 2, // set the rounded corner
      spot1: go.Spot.TopLeft,
      spot2: go.Spot.BottomRight, // make content go all the way to inside edges of rounded corners
    };
    // These nodes have text surrounded by a rounded rectangle
    // whose fill color is bound to the node data.
    // The user can drag a node by dragging its TextBlock label.
    // Dragging from the Shape will start drawing a new link.
    myDiagram.nodeTemplate = $(
      go.Node,
      "Auto",
      {
        locationSpot: go.Spot.Top,
        isShadowed: true,
        shadowBlur: 1,
        shadowOffset: new go.Point(0, 1),
        shadowColor: "rgba(0, 0, 0, .14)",
        // selection adornment to match shape of nodes
        selectionAdornmentTemplate: $(
          go.Adornment,
          "Auto",
          $(go.Shape, "RoundedRectangle", roundedRectangleParams, {
            fill: null,
            stroke: "#7986cb",
            strokeWidth: 3,
          }),
          $(go.Placeholder)
        ), // end Adornment
      },
      new go.Binding("location", "loc", go.Point.parse),
      $(
        go.Shape,
        "RoundedRectangle",
        roundedRectangleParams,
        {
          name: "SHAPE",
          fill: "#ffffff",
          strokeWidth: 0,
        },
        // gold if highlighted, white otherwise
        new go.Binding("fill", "isHighlighted", (h) =>
          h ? "gold" : "#ffffff"
        ).ofObject()
      ),
      $(
        go.Panel,
        "Vertical",
        $(
          go.Panel,
          "Horizontal",
          {
            margin: 8,
          },
          //   $(
          //     go.Picture, // flag image, only visible if a nation is specified
          //     {
          //       margin: mr8,
          //       visible: false,
          //       desiredSize: new go.Size(50, 50),
          //     },
          //     new go.Binding("source", "nation", theNationFlagConverter),
          //     new go.Binding("visible", "nation", (nat) => nat !== undefined)
          //   ),
          $(
            go.Panel,
            "Table",
            $(
              go.TextBlock,
              {
                row: 0,
                alignment: go.Spot.Left,
                font: "16px Roboto, sans-serif",
                stroke: "rgba(0, 0, 0, .87)",
                maxSize: new go.Size(160, NaN),
              },
              new go.Binding("text", "text")
            ),
            $(
              go.TextBlock,
              textStyle("appType"),
              {
                row: 1,
                alignment: go.Spot.Left,
                maxSize: new go.Size(160, NaN),
              },
              new go.Binding("text", "appType")
            ),
            $("PanelExpanderButton", "INFO", {
              row: 0,
              column: 1,
              rowSpan: 2,
              margin: ml8,
            })
          )
        ),
        $(
          go.Shape,
          "LineH",
          {
            stroke: "rgba(0, 0, 0, .60)",
            strokeWidth: 1,
            height: 1,
            stretch: go.GraphObject.Horizontal,
          },
          new go.Binding("visible").ofObject("INFO") // only visible when info is expanded
        ),
        $(
          go.Panel,
          "Vertical",
          {
            name: "INFO", // identify to the PanelExpanderButton
            stretch: go.GraphObject.Horizontal, // take up whole available width
            margin: 8,
            defaultAlignment: go.Spot.Left, // thus no need to specify alignment on each element
          },
          $(go.TextBlock, textStyle("info"), new go.Binding("text", "info"))
        )
      )
    );

    myDiagram.linkTemplate = $(
      go.Link,
      { routing: go.Link.AvoidsNodes }, // link route should avoid nodes
      $(go.Shape),
      $(go.Shape, { toArrow: "Standard" })
    );

    // Groups consist of a title in the color given by the group node data
    // above a translucent gray rectangle surrounding the member parts
    myDiagram.groupTemplate = $(
      go.Group,
      "Vertical",
      {
        selectionObjectName: "PANEL", // selection handle goes around shape, not label
        ungroupable: true,
      }, // enable Ctrl-Shift-G to ungroup a selected Group
      $(
        go.TextBlock,
        {
          font: "bold 19px sans-serif",
          isMultiline: false, // don't allow newlines in text
          editable: false, // allow in-place editing by user
        },
        new go.Binding("text", "text").makeTwoWay(),
        new go.Binding("stroke", "color")
      ),
      $(
        go.Panel,
        "Auto",
        { name: "PANEL" },
        $(
          go.Shape,
          "Rectangle", // the rectangular shape around the members
          { fill: "rgba(128,128,128,0.2)", stroke: "gray", strokeWidth: 3 }
        ),
        $(go.Placeholder, { padding: 10 }) // represents where the members are
      )
    );

    // Create the Diagram's Model:
    var nodeDataArray = [
      {
        key: 1,
        text: "App1",
        appType: "App Type 1",
        info: "Information",
        color: "#B2DFDB",
        state: "one",
        loc: "0 0",
      },
      {
        key: 2,
        text: "App 2",
        appType: "Payment Application",
        info: "Payment App",
        color: "#B2B2DB",
        state: "two",
        password: "1234",
        loc: "0 100",
      },
      {
        key: 3,
        text: "A Folder",
        color: "#1DE9B6",
        state: 2,
        group: 7,
        flag: false,
        choices: [1, 2, 3, 4, 5],
      },
      {
        key: 4,
        text: "B Folder",
        color: "#1DE9B6",
        state: 2,
        group: 7,
        flag: false,
        choices: [1, 2, 3, 4, 5],
      },
      {
        key: 5,
        text: "Services",
        color: "#1DE9B6",
        state: 2,
        group: 7,
        flag: false,
        choices: [1, 2, 3, 4, 5],
      },
      {
        key: 6,
        text: "ED Services",
        color: "#00BFA5",
        state: "three",
        flag: true,
      },
      { key: 7, text: "Group 1", color: "#00BFA5", isGroup: true },
      {
        key: 8,
        text: "A Folder",
        color: "#1DE9B6",
        state: 2,
        group: 12,
        flag: false,
        choices: [1, 2, 3, 4, 5],
      },
      {
        key: 9,
        text: "B Folder",
        color: "#1DE9B6",
        state: 2,
        group: 12,
        flag: false,
        choices: [1, 2, 3, 4, 5],
      },
      {
        key: 10,
        text: "Services",
        color: "#1DE9B6",
        state: 2,
        group: 12,
        flag: false,
        choices: [1, 2, 3, 4, 5],
      },
      {
        key: 11,
        text: "ED Services",
        color: "#00BFA5",
        state: "three",
        flag: true,
      },
      { key: 12, text: "Group 2", color: "#00BFA5", isGroup: true },
    ];
    var linkDataArray = [
      { from: 1, to: 5, color: "#5E35B1" },
      { from: 3, to: 4, color: "#6200EA" },
      { from: 3, to: 5, color: "#6200EA" },
      { from: 4, to: 5, color: "#6200EA" },
      { from: 1, to: 6, color: "#5E35B1" },
      { from: 2, to: 10, color: "#5E35B1" },
      { from: 8, to: 9, color: "#6200EA" },
      { from: 8, to: 10, color: "#6200EA" },
      { from: 9, to: 10, color: "#6200EA" },
      { from: 2, to: 11, color: "#5E35B1" },
    ];
    myDiagram.model = new go.GraphLinksModel(nodeDataArray, linkDataArray);

    // some shared model data
    myDiagram.model.modelData = { test: true, hello: "world", version: 42 };

    // select a Node, so that the first Inspector shows something
    myDiagram.select(myDiagram.nodes.first());
  }

  return (
    <Fragment>
      <div
        id="myDiagramDiv"
        style="border: solid 1px black; width:100%; height:800px;"
      ></div>
    </Fragment>
  );
}

Output:

I need to place App1 and App2 simultaneously. How do we define the position dynamically ?
I need something similar to the below images:

Wireframe Requirement:

The short answer is to set the Diagram.layout’s TreeLayout.arrangement to go.TreeLayout.ArrangementHorizontal.

It isn’t clear whether you really want there to be a group around nodes “a”, “b”, and “c”. If you do, you can achieve this result:


using:

<!DOCTYPE html>
<html>
<head>
  <title>Minimal GoJS Sample</title>
  <!-- Copyright 1998-2023 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">
const $ = go.GraphObject.make;

const myDiagram =
  $(go.Diagram, "myDiagramDiv",
    {
      layout:
        $(go.TreeLayout,
          { arrangement: go.TreeLayout.ArrangementHorizontal, angle: 90, setsPortSpot: false, setsChildPortSpot: false }),
      "undoManager.isEnabled": true
    });

myDiagram.nodeTemplate =
  $(go.Node, "Auto",
    $(go.Shape,
      { fill: "white" },
      new go.Binding("fill", "color")),
    $(go.TextBlock,
      { margin: 8 },
      new go.Binding("text"))
  );

myDiagram.groupTemplate =
  $(go.Group,
    {
      layout:
        $(go.LayeredDigraphLayout,
          { alignOption: go.LayeredDigraphLayout.AlignAll, setsPortSpots: false })
    },
    $(go.Placeholder, { padding: 10 })
  );

myDiagram.groupTemplate =
  $(go.Group, "Vertical",
    {
      layout:
        $(go.LayeredDigraphLayout,
          { alignOption: go.LayeredDigraphLayout.AlignAll, setsPortSpots: false })
    },
    $(go.TextBlock, new go.Binding("text")),
    $(go.Panel, "Auto",
      $(go.Shape, { fill: "whitesmoke" }),
      $(go.Placeholder, { padding: 10 })
    )
  );

myDiagram.model = new go.GraphLinksModel(
[
  { key: 1, text: "App1" },
  { key: 234, text: "234", isGroup: true },
  { key: 2, text: "a", group: 234 },
  { key: 3, text: "b", group: 234 },
  { key: 4, text: "c", group: 234 },
  { key: 5, text: "ED Services" },
  { key: 11, text: "App2" },
  { key: 121314, text: "121314", isGroup: true },
  { key: 12, text: "a", group: 121314 },
  { key: 13, text: "b", group: 121314 },
  { key: 14, text: "c", group: 121314 },
  { key: 15, text: "ED Services" },
],
[
  { from: 1, to: 4 },
  { from: 2, to: 3 },
  { from: 2, to: 4 },
  { from: 3, to: 4 },
  { from: 1, to: 5 },
  { from: 11, to: 14 },
  { from: 12, to: 13 },
  { from: 12, to: 14 },
  { from: 13, to: 14 },
  { from: 11, to: 15 },
]);
  </script>
</body>
</html>

If you want to use a group but hide the group while showing its members, replace the group template with:

myDiagram.groupTemplate =
  $(go.Group,
    {
      layout:
        $(go.LayeredDigraphLayout,
          { alignOption: go.LayeredDigraphLayout.AlignAll, setsPortSpots: false })
    },
    $(go.Placeholder, { padding: 10 })
  );

which results in:

If you want to put some nodes in different rows (with or without separator lines), there are several ways to do it, although all of them require some direction in the model data so that the layout knows which row each node belongs in. One possibility (except that the sample’s layout direction is horizontal, resulting in columns rather than rows): Layer Bands using a Background Part

Actually, here’s a sample where the layout is vertical, resulting in horizontal bands: Swim Bands using a Background Part

1 Like

I would like to add just the separator lines and position the nodes in the respective areas separated by separator lines as shared in the wireframe.

Is it possible to add those with same data model in the above example instead of having data model like swim bands ?

I tried editing the existing datamodel and added a myDiagram.nodeTemplateMap following the example - Swim Lanes Example. I removed the pool grouping and just added lane grouping. But the grouping happens in a different manner than it is expected.

Complete Code:

import { h, Fragment } from "preact";
import { useEffect, useState } from "preact/hooks";

declare global {
  interface Window {
    go: any;
  }
}

export function GoJsEval() {
  useEffect(() => {
    init();
  }, []); // <-- empty array means 'run once'

 
  function init() {
    const go = window.go;
    var $ = go.GraphObject.make;  // for conciseness in defining templates
    var ml8 = new go.Margin(0, 0, 0, 8);
    var myDiagram =
      $(go.Diagram, "myDiagramDiv",  // create a Diagram for the DIV HTML element
        {
          "animationManager.isEnabled": false,
          // allow double-click in background to create a new node
          "clickCreatingTool.archetypeNodeData": { text: "Node", color: "white" },
          // allow Ctrl-G to call groupSelection()
          "commandHandler.archetypeGroupData": { text: "Group", isGroup: true, color: "blue" },
          // enable undo & redo
          "undoManager.isEnabled": true,
          initialDocumentSpot: go.Spot.Top,
          initialViewportSpot: go.Spot.Top,
          // OR: Scroll to show a particular node, once the layout has determined where that node is
          // "InitialLayoutCompleted": e => {
          //  var node = e.diagram.findNodeForKey(28);
          //  if (node !== null) e.diagram.commandHandler.scrollToPart(node);
          // },
          layout:
            $(go.TreeLayout,  // use a TreeLayout to position all of the nodes
              {
                arrangement: go.TreeLayout.ArrangementHorizontal,
                isOngoing: false,  // don't relayout when expanding/collapsing panels
                treeStyle: go.TreeLayout.StyleLastParents,
                // properties for most of the tree:
                angle: 90,
                layerSpacing: 80,
                // properties for the "last parents":
                alternateAngle: 0,
                alternateAlignment: go.TreeLayout.AlignmentStart,
                alternateNodeIndent: 15,
                alternateNodeIndentPastParent: 1,
                alternateNodeSpacing: 15,
                alternateLayerSpacing: 40,
                alternateLayerSpacingParentOverlap: 1,
                alternatePortSpot: new go.Spot(0.001, 1, 20, 0),
                alternateChildPortSpot: go.Spot.Left
              })
          // automatically show the state of the diagram's model on the page
        //   "ModelChanged": e => {
        //     if (e.isTransactionFinished) {
        //       document.getElementById("savedModel").textContent = myDiagram.model.toJson();
        //     }
        //   }
        });
        function textStyle(field) {
            return [
              {
                font: "12px Roboto, sans-serif", stroke: "rgba(0, 0, 0, .60)",
                visible: false  // only show textblocks when there is corresponding data for them
              },
              new go.Binding("visible", field, val => val !== undefined)
            ];
          }

          var roundedRectangleParams = {
            parameter1: 2,  // set the rounded corner
            spot1: go.Spot.TopLeft, spot2: go.Spot.BottomRight  // make content go all the way to inside edges of rounded corners
          };
    // These nodes have text surrounded by a rounded rectangle
    // whose fill color is bound to the node data.
    // The user can drag a node by dragging its TextBlock label.
    // Dragging from the Shape will start drawing a new link.
    myDiagram.nodeTemplate = $(
        go.Node,
        "Auto",
        {
          locationSpot: go.Spot.Top,
          isShadowed: true,
          shadowBlur: 1,
          shadowOffset: new go.Point(0, 1),
          shadowColor: "rgba(0, 0, 0, .14)",
          // selection adornment to match shape of nodes
          selectionAdornmentTemplate: $(
            go.Adornment,
            "Auto",
            $(go.Shape, "RoundedRectangle", roundedRectangleParams, {
              fill: null,
              stroke: "#7986cb",
              strokeWidth: 3,
            }),
            $(go.Placeholder)
          ), // end Adornment
        },
        new go.Binding("location", "loc", go.Point.parse),
        $(
          go.Shape,
          "RoundedRectangle",
          roundedRectangleParams,
          {
            name: "SHAPE",
            fill: "#ffffff",
            strokeWidth: 0,
          },
          // gold if highlighted, white otherwise
          new go.Binding("fill", "isHighlighted", (h) =>
            h ? "gold" : "#ffffff"
          ).ofObject()
        ),
        $(
          go.Panel,
          "Vertical",
          $(
            go.Panel,
            "Horizontal",
            {
              margin: 8,
            },
          //   $(
          //     go.Picture, // flag image, only visible if a nation is specified
          //     {
          //       margin: mr8,
          //       visible: false,
          //       desiredSize: new go.Size(50, 50),
          //     },
          //     new go.Binding("source", "nation", theNationFlagConverter),
          //     new go.Binding("visible", "nation", (nat) => nat !== undefined)
          //   ),
            $(
              go.Panel,
              "Table",
              $(
                go.TextBlock,
                {
                  row: 0,
                  alignment: go.Spot.Left,
                  font: "16px Roboto, sans-serif",
                  stroke: "rgba(0, 0, 0, .87)",
                  maxSize: new go.Size(160, NaN),
                },
                new go.Binding("text", "text")
              ),
              $(
                go.TextBlock,
                textStyle("appType"),
                {
                  row: 1,
                  alignment: go.Spot.Left,
                  maxSize: new go.Size(160, NaN),
                },
                new go.Binding("text", "appType")
              ),
              $("PanelExpanderButton", "INFO", {
                row: 0,
                column: 1,
                rowSpan: 2,
                margin: ml8,
              })
            )
          ),
          $(
            go.Shape,
            "LineH",
            {
              stroke: "rgba(0, 0, 0, .60)",
              strokeWidth: 1,
              height: 1,
              stretch: go.GraphObject.Horizontal,
            },
            new go.Binding("visible").ofObject("INFO") // only visible when info is expanded
          ),
          $(
            go.Panel,
            "Vertical",
            {
              name: "INFO", // identify to the PanelExpanderButton
              stretch: go.GraphObject.Horizontal, // take up whole available width
              margin: 8,
              defaultAlignment: go.Spot.Left, // thus no need to specify alignment on each element
            },
            $(
              go.TextBlock,
              textStyle("info"),
              new go.Binding("text", "info")
            ),
          )
        )
      );

      var HORIZONTAL = false
      myDiagram.nodeTemplateMap.add("Lane",
        $(go.Part, "Position",
          // new go.Binding("itemArray"),
          {
            isLayoutPositioned: false,  // but still in document bounds
            locationSpot: new go.Spot(0, 0, HORIZONTAL ? 0 : 16, HORIZONTAL ? 16 : 0),  // account for header height
            layerName: "Background",
            pickable: false,
            selectable: false,
            itemTemplate:
              $(go.Panel, HORIZONTAL ? "Vertical" : "Horizontal",
                new go.Binding("position", "bounds", b => b.position),
                // $(go.TextBlock,
                //   {
                //     angle: HORIZONTAL ? 0 : 270,
                //     textAlign: "center",
                //     wrap: go.TextBlock.None,
                //     font: "bold 11pt sans-serif",
                //     background: $(go.Brush, "Linear", { 0: "aqua", 1: go.Brush.darken("aqua") })
                //   },
                //   new go.Binding("text"),
                //   // always bind "width" because the angle does the rotation
                //   new go.Binding("width", "bounds", r => HORIZONTAL ? r.width : r.height)
                // ),
                // option 1: rectangular bands:
                // $(go.Shape,
                //   { stroke: null, strokeWidth: 0 },
                //   new go.Binding("desiredSize", "bounds", r => r.size),
                //   new go.Binding("fill", "itemIndex", i => i % 2 == 0 ? "whitesmoke" : go.Brush.darken("whitesmoke")).ofObject())
                // option 2: separator lines:
                (HORIZONTAL
                 ? $(go.Shape, "LineV",
                     { stroke: "gray", alignment: go.Spot.TopLeft, width: 1 },
                     new go.Binding("height", "bounds", r => r.height),
                     new go.Binding("visible", "itemIndex", i => i > 0).ofObject())
                 : $(go.Shape, "LineH",
                     { stroke: "gray", alignment: go.Spot.TopLeft, height: 1 },
                     new go.Binding("width", "bounds", r => r.width),
                     new go.Binding("visible", "itemIndex", i => i > 0).ofObject())
                )
              )
          }
        ));
  
    myDiagram.linkTemplate =
  $(go.Link,
    { routing: go.Link.AvoidsNodes },  // link route should avoid nodes
    $(go.Shape),
    $(go.Shape, { toArrow: "Standard" })
  );

    // Create the Diagram's Model:
    var nodeDataArray =   [
        { key: "Lane1", text: "Lane1", isGroup: true, category: "Lane",  color: "lightblue" },
        { key: "Lane2", text: "Lane2", isGroup: true, category: "Lane", color: "lightgreen" },
        { key: "Lane3", text: "Lane3", isGroup: true, category: "Lane", color: "lightyellow" },
        { key: 1, text: "App1", appType: "App Type 1", info: "Information", color: "#B2DFDB", state: "one", loc: "0 0", group: "Lane1" },
        { key: 2, text: "App 2", appType: "Payment Application", info: "Payment App", color: "#B2B2DB", state: "two", password: "1234", loc: "0 100", group: "Lane1" },
        { key: 3, text: "A Folder", color: "#1DE9B6", state: 2,  flag: false, choices: [1, 2, 3, 4, 5], group: "Lane2" },
         { key: 4, text: "B Folder", color: "#1DE9B6", state: 2,  flag: false, choices: [1, 2, 3, 4, 5], group: "Lane2" },
          { key: 5, text: "Services", color: "#1DE9B6", state: 2,  flag: false, choices: [1, 2, 3, 4, 5], group: "Lane2" },
        { key: 6, text: "ED Services", color: "#00BFA5", state: "three", flag: true, group: "Lane3" },
        // { key: 7, text: "Group 1", color: "#00BFA5", isGroup: true },
        { key: 8, text: "A Folder", color: "#1DE9B6", state: 2, flag: false, choices: [1, 2, 3, 4, 5], group: "Lane2" },
        { key: 9, text: "B Folder", color: "#1DE9B6", state: 2,  flag: false, choices: [1, 2, 3, 4, 5], group: "Lane2" },
         { key: 10, text: "Services", color: "#1DE9B6", state: 2,  flag: false, choices: [1, 2, 3, 4, 5], group: "Lane2" },
       { key: 11, text: "ED Services", color: "#00BFA5", state: "three", flag: true , group: "Lane3"},
      ]
    var linkDataArray = [
      { from: 1, to: 5, color: "#5E35B1" },
      { from: 3, to: 4, color: "#6200EA" },
      { from: 3, to: 5, color: "#6200EA" },
      { from: 4, to: 5, color: "#6200EA" },
      { from: 1, to: 6, color: "#5E35B1" },
      { from: 2, to: 10, color: "#5E35B1" },
      { from: 8, to: 9, color: "#6200EA" },
      { from: 8, to: 10, color: "#6200EA" },
      { from: 9, to: 10, color: "#6200EA" },
      { from: 2, to: 11, color: "#5E35B1" }
    ];
    myDiagram.model = new go.GraphLinksModel(nodeDataArray, linkDataArray);

    // some shared model data
    myDiagram.model.modelData = { test: true, hello: "world", version: 42 };

    // select a Node, so that the first Inspector shows something
    myDiagram.select(myDiagram.nodes.first());
  }

  return (
    <Fragment>
      <div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:800px;"></div>
    </Fragment>
  );
}



Output:

I don’t think the Group-oriented kinds of samples are suitable for your requirements. I’ll adapt a bands-oriented sample, when I get some free time later today.

1 Like

Okay, Thank You. The thing is i should be able to have link between nodes in the same band. I am attaching the wireframe just for the reference.

I am working on evaluating the gojs for my requirement. It would be great if you could help me on this as i am running on timeline.

Also, I have a question with respect to Org Chart or Family Tree. Can we use Org Chart and address the below concerns

  • Can a child item have multiple parents ?

  • Can a one or more child in the same band have links ?

  • Can the nodes be separated separator lines and placed accordingly ?

If you want nodes to possibly have multiple “parents”, then it won’t be a tree structured graph, so you probably do not want to use TreeLayout any more, but instead LayeredDigraphLayout.

Your model data doesn’t match what you are showing in your sketch. I have added separate groups to hold the “a”, “b”, and “c” folder nodes. And I combined the separate lane nodes to be an Array in a single “_BANDS” data object of template category “Bands”.

And your templates seem to imply a different kind of bindings of colors. But I didn’t bother using your node template anyway, since those details shouldn’t matter for what you are asking about.

The following code produces this:

<!DOCTYPE html>
<html>
<head>
  <title>Banded LayeredDigraph Sample</title>
  <!-- Copyright 1998-2023 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 src="https://gojs.net/extras/BandedLayeredDigraphLayout.js"></script>
  <script id="code">
const $ = go.GraphObject.make;

const myDiagram =
  $(go.Diagram, "myDiagramDiv",
    {
      layout: $(BandedLayeredDigraphLayout, { direction: 90, setsPortSpots: false }),  // custom layout is defined above
      "undoManager.isEnabled": true
    });

myDiagram.nodeTemplate =
  $(go.Node, "Auto",
    { fromSpot: go.Spot.AllSides, toSpot: go.Spot.AllSides },
    $(go.Shape,
      { fill: "white" },
      new go.Binding("fill", "color")),
    $(go.TextBlock,
      { margin: 8 },
      new go.Binding("text"))
  );

myDiagram.linkTemplate =
  $(go.Link,
    { routing: go.Link.AvoidsNodes, corner: 5 },
    $(go.Shape, { strokeWidth: 2 },
      new go.Binding("stroke", "color")),
    $(go.Shape, { toArrow: "Standard", strokeWidth: 0 },
      new go.Binding("fill", "color"))
  );

myDiagram.groupTemplate =
  $(go.Group, "Auto",
    { layout: $(go.LayeredDigraphLayout, { alignOption: go.LayeredDigraphLayout.AlignAll, setsPortSpots: false }) },
    { avoidable: false, fromSpot: go.Spot.AllSides, toSpot: go.Spot.AllSides },
    $(go.Shape, "RoundedRectangle",
      { parameter1: 20, fill: "transparent" }),
    $(go.Placeholder, { padding: 10 })
  );

// There should be at most a single object of this category.
// This Part will be modified by commitLayers to display visual "bands"
// where each "layer" is a layer of the graph.
// This template is parameterized at load time by the HORIZONTAL parameter.
// You also have the option of showing rectangles for the layer bands or
// of showing separator lines between the layers, but not both at the same time,
// by commenting in/out the indicated code.
const HORIZONTAL = false;
myDiagram.nodeTemplateMap.add("Bands",
  $(go.Part, "Position",
    new go.Binding("itemArray"),  // Array of band descriptor objects
    {
      isLayoutPositioned: false,  // but still in document bounds
      locationSpot: new go.Spot(0, 0, HORIZONTAL ? 0 : 146, HORIZONTAL ? 46 : 0),  // account for header height/width
      layerName: "Background",
      pickable: false,
      selectable: false,
      itemTemplate:
        $(go.Panel, HORIZONTAL ? "Vertical" : "Horizontal",
          { visible: false },  // hide bands without nodes or vertexes in them
          new go.Binding("visible", "bounds", b => b.width > 0 && b.height > 0),
          new go.Binding("position", "bounds", b => b.position),
          $(go.Panel, "Auto",  // the header
            new go.Binding("desiredSize", "bounds", r => HORIZONTAL ? new go.Size(r.width, 46) : new go.Size(146, r.height)),
            $(go.Shape, { fill: "transparent", strokeWidth: 0 }),
            $(go.TextBlock,
              { font: "bold 14pt sans-serif" },
              new go.Binding("text"))
          ),
          $(go.Panel,
            // option 1: rectangular bands:
            $(go.Shape,
              { fill: "transparent", strokeWidth: 0 },
              new go.Binding("desiredSize", "bounds", r => r.size),
              new go.Binding("fill", "color")),
            // option 2: separator lines:
            (HORIZONTAL
              ? $(go.Shape, "LineV",
                  { stroke: "#AAAAAA", alignment: go.Spot.TopLeft, width: 1 },
                  new go.Binding("height", "bounds", r => r.height),
                  new go.Binding("visible", "itemIndex", i => i > 0).ofObject())
              : $(go.Shape, "LineH",
                  { stroke: "#AAAAAA", alignment: go.Spot.TopLeft, height: 1 },
                  new go.Binding("width", "bounds", r => r.width),
                  new go.Binding("visible", "itemIndex", i => i > 0).ofObject())
            )
          )
        )
    }
  ));


myDiagram.model = new go.GraphLinksModel(
  [
    { key: "_BANDS", category: "Bands",
      itemArray: [
        { index: 0, text: "Lane1", color: "lightblue" },
        { index: 1, text: "Lane2", color: "lightgreen" },
        { index: 2, text: "Lane3", color: "lightyellow" },
      ]
    },
    { key: 1, text: "App1", appType: "App Type 1", info: "Information", color: "#B2DFDB", state: "one", loc: "0 0", band: "Lane1" },
    { key: 2, text: "App 2", appType: "Payment Application", info: "Payment App", color: "#B2B2DB", state: "two", password: "1234", loc: "0 100", band: "Lane1" },
    { key: 34, isGroup: true, band: "Lane2" },
    { key: 3, text: "A Folder", color: "#1DE9B6", state: 2,  flag: false, choices: [1, 2, 3, 4, 5], group: 34 },
    { key: 4, text: "B Folder", color: "#1DE9B6", state: 2,  flag: false, choices: [1, 2, 3, 4, 5], group: 34 },
    { key: 41, text: "C Folder", color: "#1DE9B6", state: 2,  flag: false, choices: [1, 2, 3, 4, 5], group: 34 },
    { key: 5, text: "Services", color: "#1DE9B6", state: 2,  flag: false, choices: [1, 2, 3, 4, 5], band: "Lane2" },
    { key: 6, text: "ED Services", color: "#00BFA5", state: "three", flag: true, band: "Lane3" },
    { key: 7, isGroup: true, band: "Lane2" },
    { key: 8, text: "A Folder", color: "#1DE9B6", state: 2, flag: false, choices: [1, 2, 3, 4, 5], group: 7 },
    { key: 9, text: "B Folder", color: "#1DE9B6", state: 2,  flag: false, choices: [1, 2, 3, 4, 5], group: 7 },
    { key: 10, text: "Services", color: "#1DE9B6", state: 2,  flag: false, choices: [1, 2, 3, 4, 5], band: "Lane2" },
    { key: 11, text: "ED Services", color: "#00BFA5", state: "three", flag: true , band: "Lane3"},
  ],
  [
    { from: 1, to: 5, color: "#5E35B1" },
    { from: 3, to: 4, color: "#6200EA" },
    { from: 3, to: 5, color: "#6200EA" },
    { from: 3, to: 41, color: "#6200EA" },
    { from: 4, to: 41, color: "#6200EA" },
    { from: 1, to: 6, color: "#5E35B1" },
    { from: 2, to: 10, color: "#5E35B1" },
    { from: 8, to: 9, color: "#6200EA" },
    { from: 8, to: 10, color: "#6200EA" },
    { from: 9, to: 10, color: "#6200EA" },
    { from: 2, to: 11, color: "#5E35B1" }
  ]);
  </script>
</body>
</html>

You’ll want to download the definition of BandedLayeredDigraphLayout that is at https://gojs.net/extras/BandedLayeredDigraphLayout.js

1 Like

@walter - Thank You, Will look into it.

A post was split to a new topic: Conditionally showing Picture if data.source is present

Also, Since the links(arrow line) are overlapped with separator lines mostly, we are not able to visualize the separator line clearly. Is it possible to bring arrows a little bit down instead of getting it overlapped with separator lines ?

And can we add text in the links ?

The sample I just gave you has both rectangle bands as well as separator lines.
If I comment out those colored rectangles and shift the separator lines down a little bit, you get:

Here’s the changed code:

          //$(go.Panel,
            // option 1: rectangular bands:
            // $(go.Shape,
            //   { fill: "transparent", strokeWidth: 0 },
            //   new go.Binding("desiredSize", "bounds", r => r.size),
            //   new go.Binding("fill", "color")),
            // option 2: separator lines:
            (HORIZONTAL
              ? $(go.Shape, "LineV",
                  { stroke: "#AAAAAA", alignment: new go.Spot(0, 0, 5, 0), width: 1 },
                  new go.Binding("height", "bounds", r => r.height),
                  new go.Binding("visible", "itemIndex", i => i > 0).ofObject())
              : $(go.Shape, "LineH",
                  { stroke: "#AAAAAA", alignment: new go.Spot(0, 0, 0, 5), height: 1 },
                  new go.Binding("width", "bounds", r => r.width),
                  new go.Binding("visible", "itemIndex", i => i > 0).ofObject())
            )
          //)

For text on links, please read GoJS Link Labels -- Northwoods Software

Hi @walter ,

What does choices array and flag property in data model used for ?

{ key: 1, text: "App1", appType: "App Type 1", info: "Information", color: "#B2DFDB", state: "one", loc: "0 0", band: "Lane1" },
{ key: 2, text: "App 2", appType: "Payment Application", info: "Payment App", color: "#B2B2DB", state: "two", password: "1234", loc: "0 100", band: "Lane1" },
{ key: 34, isGroup: true, band: "Lane2" },
{ key: 3, text: "A Folder", color: "#1DE9B6", state: 2,  flag: false, choices: [1, 2, 3, 4, 5], group: 34 },
{ key: 4, text: "B Folder", color: "#1DE9B6", state: 2,  flag: false, choices: [1, 2, 3, 4, 5], group: 34 },
{ key: 41, text: "C Folder", color: "#1DE9B6", state: 2,  flag: false, choices: [1, 2, 3, 4, 5], group: 34 },
{ key: 5, text: "Services", color: "#1DE9B6", state: 2,  flag: false, choices: [1, 2, 3, 4, 5], band: "Lane2" },
{ key: 6, text: "ED Services", color: "#00BFA5", state: "three", flag: true, band: "Lane3" },
{ key: 7, isGroup: true, band: "Lane2" },
{ key: 8, text: "A Folder", color: "#1DE9B6", state: 2, flag: false, choices: [1, 2, 3, 4, 5], group: 7 },
{ key: 9, text: "B Folder", color: "#1DE9B6", state: 2,  flag: false, choices: [1, 2, 3, 4, 5], group: 7 },
{ key: 10, text: "Services", color: "#1DE9B6", state: 2,  flag: false, choices: [1, 2, 3, 4, 5], band: "Lane2" },
{ key: 11, text: "ED Services", color: "#00BFA5", state: "three", flag: true , band: "Lane3"},

Whenever you see properties in the data, other than standard properties such as “key” or “group” (for node data) or “to” or “from” (for link data), you should look at the template to decide what its purpose is.

I may have just copied the data from some other sample where those properties were meaningful and forgotten to have cleaned them up. You can delete them.

Okay, Thank You