The node templates in the Logic Circuit sample each have a fixed number of ports. For example:
var andTemplate =
$(go.Node, "Spot", nodeStyle(),
$(go.Shape, "AndGate", shapeStyle()),
$(go.Shape, "Rectangle", portStyle(true),
{ portId: "in1", alignment: new go.Spot(0, 0.3) }),
$(go.Shape, "Rectangle", portStyle(true),
{ portId: "in2", alignment: new go.Spot(0, 0.7) }),
$(go.Shape, "Rectangle", portStyle(false),
{ portId: "out", alignment: new go.Spot(1, 0.5) })
);
It is possible to add or remove ports from a particular instance of a Node, but is that really what you want? Adding or removing an element (that happens to be a port) won't modify the model, so when you save and then re-load the model, those changes will have disappeared.
If you want a variable number of ports on a node, you need to define the node template to support a variable number of ports, where the node data object has one or more properties that are Arrays of port descriptor objects. Each port descriptor would have information about identifier, name, shape, color, position, and perhaps other parameters.
Then there would be one or more Panels whose Panel.itemArray is data bound to the corresponding Array property. The Panel.itemTemplate would declare how each item (i.e. port) is visualized. Please read: https://gojs.net/latest/intro/itemArrays.html
For an example of this with four item Arrays, one for each side of a Node, please examine this sample: https://gojs.net/latest/samples/dynamicPorts.html
That sample also demonstrates various ways in which the user can add or remove or move ports dynamically.
In that sample there is only a single node template, but it's more complex because it supports four separate Arrays of port descriptor objects, one for each side of the Node. That node template looks like:
myDiagram.nodeTemplate =
$(go.Node, "Table",
{
locationObjectName: "BODY",
locationSpot: go.Spot.Center,
selectionObjectName: "BODY",
contextMenu: nodeMenu
},
new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
// the body
$(go.Panel, "Auto",
{
row: 1, column: 1, name: "BODY",
stretch: go.GraphObject.Fill
},
$(go.Shape, "Rectangle",
{
fill: "#dbf6cb", stroke: null, strokeWidth: 0,
minSize: new go.Size(60, 60)
}),
$(go.TextBlock,
{ margin: 10, textAlign: "center", font: "bold 14px Segoe UI,sans-serif", stroke: "#484848", editable: true },
new go.Binding("text", "name").makeTwoWay())
), // end Auto Panel body
// the Panel holding the left port elements, which are themselves Panels,
// created for each item in the itemArray, bound to data.leftArray
$(go.Panel, "Vertical",
new go.Binding("itemArray", "leftArray"),
{
row: 1, column: 0,
itemTemplate:
$(go.Panel,
{
_side: "left", // internal property to make it easier to tell which side it's on
fromSpot: go.Spot.Left, toSpot: go.Spot.Left,
fromLinkable: true, toLinkable: true, cursor: "pointer",
contextMenu: portMenu
},
new go.Binding("portId", "portId"),
$(go.Shape, "Rectangle",
{
stroke: null, strokeWidth: 0,
desiredSize: portSize,
margin: new go.Margin(1, 0)
},
new go.Binding("fill", "portColor"))
) // end itemTemplate
}
), // end Vertical Panel
... three more Panels with itemArray Bindings ...