Node template with attribute for several ports

Is it possible to create a node template where an attribute with the same value for several ports will be presented once, as in the attached drawing:
test

What is the node data object for this node? You can leave out everything but the ports and needed attributes. I’m wondering what must be calculated and what is given.

Would there be cases involving non-contiguous attributes? For example where an “A” attribute port would be added at the bottom below the “C” ports.

I just want this port attribute (atrA,atrB,atrC) to be presented nicely, I will not add a new port or edit an existing port. Ports with the same attribute (atrA,atrB,atrC) will be next to each other.
Node data:
node={key,name,portList[]} port={portId, name, attr}

So deciding what to show for the common attributes needs to be computed, not declared in the node data?

I would like to show this attribute (attr) stretched over several ports,
I don’t need calculating anything.
But I have a problem with this.
Can you write a node template?

I just want confirmation that the node data does not include any declarative information saying exactly what needs to be drawn for the common attributes. That it can compute those common attribute regions from the port data items Array and decide what to show there based on that array.

If it makes things easier, I could add some information about region to node data, but from my point of view it’s better to get the data from the ports array

OK, I’ve taken a couple shortcuts:

  • I assume each port has the same height, expediently hardcoded as 15, but you could easily parameterize it
  • I assume each port descriptor (i.e. item in the “ports” Array) declares the index at which it should be
<!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">
const $ = go.GraphObject.make;

const myDiagram =
  $(go.Diagram, "myDiagramDiv");

myDiagram.nodeTemplate =
  $(go.Node,
    $(go.Panel, "Table",
      $(go.Shape,
        { column: 0, columnSpan: 2, fill: "white", stretch: go.GraphObject.Fill },
        new go.Binding("fill", "color")),
      $(go.Panel, "Table",
        { column: 1, alignment: go.Spot.Right, margin: 2 },
        new go.Binding("itemArray", "ports", convertPortsToAttributes),
        {
          itemTemplate:
            $(go.Panel, "Auto",
              new go.Binding("row", "index"),
              { width: 30, height: 15 },
              new go.Binding("height", "rows", r => r*15),
              $(go.Shape,
                { fill: "white", stretch: go.GraphObject.Fill },
                new go.Binding("fill", "color")),
              $(go.TextBlock,
                { margin: new go.Margin(1, 0, 0, 0) },
                new go.Binding("text"))
            )
        }
      ),
      $(go.Panel, "Table",
        { column: 2 },
        new go.Binding("itemArray", "ports"),
        {
          itemTemplate:
            $(go.Panel, "Spot",
              new go.Binding("portId", "id"),
              new go.Binding("row", "index"),
              { width: 30, height: 15 },
              $(go.Shape,
                { fill: "white" },
                new go.Binding("fill", "color")),
              $(go.TextBlock,
                { margin: new go.Margin(1, 0, 0, 0) },
                new go.Binding("text", "id"))
            )
        }
      ),
    )
  );

function convertPortsToAttributes(arr) {
  const a2 = [];
  let prevText = "";
  arr.forEach(port => {
    if (port.text !== prevText) {
      a2.push({ index: port.index, rows: 1, text: port.text, color: port.color });
      prevText = port.text;
    } else {
      a2[a2.length-1].rows++;
    }
  });
  return a2;
}

myDiagram.model = new go.GraphLinksModel({
  linkFromPortIdProperty: "fpid",
  linkToPortIdProperty: "tpid",
  nodeDataArray: [
      {
        key: 1, text: "Alpha", color: "lightblue",
        ports: [
          { id: "a1", index: 0, text: "A", color: "yellow" },
          { id: "a2", index: 1, text: "A", color: "yellow" },
          { id: "a3", index: 2, text: "A", color: "yellow" },
          { id: "a4", index: 3, text:"A", color: "yellow" },
          { id: "b1", index: 4, text:"B", color: "lightgreen" },
          { id: "b2", index: 5, text:"B", color: "lightgreen" },
          { id: "c1", index: 6, text:"C", color: "orange" },
        ]
      },
      {
        key: 2, text:"Beta", color: "lightblue",
        ports: [
          { id: "a1", index: 0, text:"A", color: "yellow" },
          { id: "b1", index: 1, text:"B", color: "lightgreen" },
          { id: "a2", index: 2, text:"A", color: "yellow" },
          { id: "a3", index: 3, text:"A", color: "yellow" },
        ]
      },
    ]
  }
);
  </script>
</body>
</html>

The result:
image

Note how in the second node’s data I purposely inserted a “B” type port into the list of “A” type ports.

good job, thanks walter