Although it is very common to use a Placeholder inside a Group, it is not required. Using a Shape, for example, instead of a Placeholder permits features such as having a group maintain a fixed size, independent of the sizes and positions of its member nodes, and even when there are no member nodes at all. It also may allow the user to resize the "area" if that functionality is desired.
Not using a Placeholder in a Group means that you have to maintain the size and position of the group, because it cannot depend on the size and position of its member nodes. In these examples we will explicitly set and/or bind the Part.location of the nodes, including the groups. The Shape that replaces the Placeholder in the group's template should also get its GraphObject.desiredSize set or bound.
diagram.nodeTemplate =
new go.Node("Auto")
.bind("location", "loc", go.Point.parse)
.add(
new go.Shape("Ellipse", { fill: "white" }),
new go.TextBlock()
.bind("text")
);
diagram.groupTemplate =
new go.Group("Vertical", {
selectionObjectName: "PH",
locationObjectName: "PH"
})
.bind("location", "loc", go.Point.parse)
.add(
new go.TextBlock({ font: "Bold 12pt Sans-Serif" }) // group title
.bind("text"),
new go.Shape({ // using a Shape instead of a Placeholder
name: "PH",
fill: "lightyellow"
})
.bind("desiredSize", "size", go.Size.parse)
);
const nodeDataArray = [
{ key: 1, text: "Alpha", loc: "0 0" },
{ key: 2, text: "Beta", group: 4, loc: "60 60" },
{ key: 3, text: "Gamma", group: 4, loc: "125 75" },
{ key: 4, text: "Omega", isGroup: true, loc: "50 50", size: "150 50" },
{ key: 5, text: "Delta", loc: "200 0" }
];
const linkDataArray = [
{ from: 1, to: 2 }, // from outside the Group to inside it
{ from: 2, to: 3 }, // this link is a member of the Group
{ from: 4, to: 5 } // from the Group to a Node
];
diagram.model = new go.GraphLinksModel(nodeDataArray, linkDataArray);
diagram.select(diagram.findNodeForKey(4));
Note that moving the "Beta" or "Gamma" nodes does not change the size or position of the "Omega" group. However moving or copying or deleting the group includes those member nodes in the operation.
One can control where the user may drag member nodes. For example, the Swim Lanes sample demonstrates a custom Part.dragComputation function that limits the motion of a member node to stay within its containing group.
You can make the main shape resizable by the user. (At the current time groups are not rotatable.)
This example also makes the Part.location and GraphObject.desiredSize data bindings TwoWay, so that as the user moves groups or resizes their main shapes, the data in the model is updated automatically.
diagram.nodeTemplate =
new go.Node()
.bindTwoWay("location", "loc", go.Point.parse, go.Point.stringify)
.add(
new go.TextBlock()
.bind("text")
);
diagram.groupTemplate =
new go.Group("Vertical", {
selectionObjectName: "PH",
locationObjectName: "PH",
resizable: true,
resizeObjectName: "PH"
})
.bindTwoWay("location", "loc", go.Point.parse, go.Point.stringify)
.add(
new go.TextBlock({ font: "Bold 12pt Sans-Serif" }) // group title
.bind("text"),
new go.Shape({ // using a Shape instead of a Placeholder
name: "PH",
fill: "lightyellow"
})
.bindTwoWay("desiredSize", "size", go.Size.parse, go.Size.stringify)
);
const nodeDataArray = [
{ key: 1, text: "Alpha", loc: "0 0" },
{ key: 2, text: "Beta", group: 4, loc: "60 60" },
{ key: 3, text: "Gamma", group: 4, loc: "125 75" },
{ key: 4, text: "Omega", isGroup: true, loc: "50 50", size: "150 50" },
{ key: 5, text: "Delta", loc: "200 0" }
];
const linkDataArray = [
{ from: 1, to: 2 }, // from outside the Group to inside it
{ from: 2, to: 3 }, // this link is a member of the Group
{ from: 4, to: 5 } // from the Group to a Node
];
diagram.model = new go.GraphLinksModel(nodeDataArray, linkDataArray);
diagram.select(diagram.findNodeForKey("Omega"));
It is also possible to control how the user resizes a group. For example, the Swim Lanes sample demonstrates a custom ResizingTool that limits how small each lane can go. It also demonstrates a custom Adornment that has only two resize handles.
You do not have to use Groups as the only mechanism by which to organize a collection of Parts. For example, the Layer Bands sample demonstrates how some Layouts can be customized to automatically maintain the positions and sizes of special parts that are in the background, appearing to surround the nodes that belong to each layout layer.
Not using Groups also means that it becomes possible to avoid some of the restrictions inherent in Groups, such as the limitation that each Part can have at most one Part.containingGroup. The Shared States sample demonstrates how one can make it appear that more than one "group" can contain a node. However, this requires some additional custom Tools and custom Layouts, or always explicitly setting/binding the location and size of every node and "group".