Left align nodes inside their cells (TableLayout)

I have a diagram as shown in the screenshot below. All of the green boxes are table layouts, so what we have here is a nested table layout structure. Now there is no fixed width for a table layout, so each box occupies as much width as determined by the number and size of its children. The first child of my outermost tableLayout box, labelled 1, resides in the first row’s first column, and determines the size for rest of the cells inside the container. Therefore, its siblings labelled 2, 3, 4 and 5, all residing on different rows inside cells whose widths are equal to the group labelled 1, are centred aligned and appear to have a lot of spacing around them, because their widths are much smaller than the cells that they occupy. Is there a way to left-align them inside their cells, as indicated by the red arrows?

Here is the template for my TableLayout:

const tableLayout = $(gojs.Group, "Vertical", new gojs.Binding("location", "loc", gojs.Point.parse).makeTwoWay(gojs.Point.stringify),
        { 
            layout: $(TableLayout),
            // margin: new gojs.Margin(4, 4), 
            // padding: new gojs.Margin(8, 4),
            defaultAlignment: gojs.Spot.Left, 
            minSize: new gojs.Size(100,50)
        },
        new gojs.Binding("row", "row"),
        new gojs.Binding('column', 'col'),
        new gojs.Binding('columnSpan', 'colSpan'),
        $(gojs.Panel, "Horizontal", // the header
                { defaultAlignment: gojs.Spot.Top, padding: new gojs.Margin(8, 4) },
                $("SubGraphExpanderButton"),  // this Panel acts as a Button
                $(gojs.TextBlock,     // group title near top, next to button
                    { font: "Bold 12pt Sans-Serif", margin: new gojs.Margin(0, 4), maxSize: new gojs.Size(150,50) },
                    new gojs.Binding("text", "key", (data) => {
                        return `Table: ${data}`
                    })
                ),
            ),
        $(gojs.Panel, "Auto",
            $(gojs.Shape, "RoundedRectangle", { fill: null}),
            $(gojs.Placeholder, {padding: 8,})
        )
    )

Set { alignment: go.Spot.Left } on the Group and on each Node.

Thanks Walter. The issue is resolved somewhat, but there are still some unexpected margins. I can’t make out where they are coming from.

Here’s my current example:

<!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>
  <textarea id="mySavedModel" style="width:100%;height:250px"></textarea>

  <script src="go.js"></script>
  <script src="../extensions/TableLayout.js"></script>
  <script id="code">
const $ = go.GraphObject.make;

const myDiagram =
  $(go.Diagram, "myDiagramDiv",
    {
      "undoManager.isEnabled": true,
      "ModelChanged": e => {     // just for demonstration purposes,
        if (e.isTransactionFinished) {  // show the model data in the page's TextArea
          document.getElementById("mySavedModel").textContent = e.model.toJson();
        }
      }
    });

myDiagram.nodeTemplate =
  $(go.Node, "Auto",
    {
      alignment: go.Spot.Left,
      margin: 4
    },
    new go.Binding("row", "row"),  // these all affect TableLayout of containing Group
    new go.Binding('column', 'col'),
    new go.Binding('columnSpan', 'colSpan'),
    $(go.Shape,
      { fill: "white" },
      new go.Binding("fill", "color")),
    $(go.TextBlock,
      { margin: 8 },
      new go.Binding("text", "key"))
  );

myDiagram.groupTemplate =
  $(go.Group, "Vertical",
    new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
    { 
      alignment: go.Spot.Left,  // affects TableLayout of containing group
      margin: 4,  // affects TableLayout of containing group
      layout: $(TableLayout),  // affects arrangement of member nodes
      minSize: new go.Size(100, 50),  // affects this panel (i.e. whole group)
      defaultStretch: go.GraphObject.Horizontal  // affects elements of this panel
    },
    new go.Binding("row", "row"),  // these all affect TableLayout of containing Group
    new go.Binding('column', 'col'),
    new go.Binding('columnSpan', 'colSpan'),
    // the header
    $(go.Panel, "Table",
      { padding: new go.Margin(8, 4), background: "green" },
      new go.Binding("background", "color"),
      $(go.Panel, "Horizontal",
        $("SubGraphExpanderButton"),
        $(go.TextBlock,
          { font: "bold 12pt sans-serif", margin: new go.Margin(0, 4) },
          new go.Binding("text", "", data => `Table: ${data.key}`)
        )
      )
    ),
    // the body holding the member nodes
    $(go.Panel, "Auto",
      $(go.Shape, { fill: null }),
      $(go.Placeholder, { padding: 8, alignment: go.Spot.Left })
    )
  );

myDiagram.model = new go.GraphLinksModel(
[
  { key: 1, isGroup: true },
  { key: 2, isGroup: true, group: 1, row: 0, col: 0 },
  { key: 3, isGroup: true, group: 1, row: 0, col: 1  },
  { key: 4, isGroup: true, group: 1, row: 0, col: 2  },
  { key: 5, isGroup: true, group: 1, row: 1, col: 0  },
  { key: 6, isGroup: true, group: 1, row: 1, col: 1  },
  { key: 7, isGroup: true, group: 6, row: 0, col: 0  },
  { key: 8, isGroup: true, group: 6, row: 0, col: 1  },
  { group: 2, row: 0, col: 0 },
  { group: 2, row: 0, col: 1 },
  { group: 2, row: 0, col: 2 },
  { group: 3, row: 0, col: 0 },
  { group: 3, row: 0, col: 1 },
  { group: 3, row: 0, col: 2 },
  { group: 5, row: 0, col: 0 },
  { group: 5, row: 0, col: 1 },
  { group: 5, row: 0, col: 2 },
]);
  </script>
</body>
</html>

Thanks for providing the sample. I have replaced my template with the one that you provided, and those issues are gone. However, I am still presented with two problems:

  1. When I add nodes inside this table layout, each node containing properties for “row” and “column” inside it, they get mixed up and render on top of each other. When I simply display those nodes as groups, they render fine. Here is the template for my nodes:
const defaultNode =
        // 
        $(gojs.Node, "Auto",
             new gojs.Binding('location', 'loc', gojs.Point.parse).makeTwoWay(gojs.Point.stringify),
            {
                padding: 1,
            },
            $(gojs.Panel, "Auto" ,
                $(gojs.Shape, "RoundedRectangle", { parameter1: 4, fill: "red", stroke: "#424242" }),
                $(gojs.TextBlock, new gojs.Binding("text", "text"),
                    {
                        width: 150,
                        font: '12pt sans-serif', stroke: "#424242"
                    })
            )
        );

2: The second issue is that as you can see in the image below, when groups inside a table row are of variable width, they tend to overlap. This is not the case when they have roughly similar widths.

If the nodes are being added to a group that is using TableLayout, then you need to assign or bind the row and column properties on the node. I have updated the code that I posted above.

Thanks, that did resolve the issue. And do you have any insights on how I might be able to resolve the second issue (the overlapping groups) ?

How can I reproduce the problem?

I believe having 5 groups in a single row, each having different width, should reproduce this. Try to set larger widths.

It works well:

Hmmm this is weird. Do you think it might have something to do with the fact that I am performing my layouts on NodeJs on the backend?

Also, could you please try to give the containers bigger names that cause its width to expand, instead of adding more nodes to achieve the same effect? That was so, in my case so maybe this is the reason.

Ah, so that was a problem all along – on the server there is no accurate measurement of text unless using a headless browser such as Puppeteer. GoJS in Node.js -- Northwoods Software

I see. So could setting a maxSize or some similar property on text boxes solve this problem?

What happened when you tried it?