Grouping Nodes
The classic graph model is extended so that nodes can have other nodes as their parent. The parent of a node is referred to as a group node, and nodes with a common parent are considered as being in the same group or being grouped.
All nodes have a parent. This can either be another node or null
if the node is top-level and not
contained in any group node.
IGraph provides methods to create new nodes as group nodes. It also supports creating new nodes as child nodes of a given group node.
- createNode(parent: INode, layout: Rect, style: INodeStyle, tag: Object): INode
- Creates a new node as a child of an existing group node.
- createGroupNode(parent: INode, layout: Rect, style: INodeStyle, tag: Object): INode
- Creates a new group node. Setting the
parent
parameter to an existing node will create the new node as a child of the given parent node, whereas setting it tonull
will create the new node top-level, i.e., as a direct child of the graph. The new node’s properties will get the default values as defined in the groupNodeDefaults if they are not passed as parameters.
As shown in section Setting Defaults for new Items, a group node has different defaults than normal (leaf) nodes. For example, it is possible to use different node styles and label placements for group and normal nodes:
// set the defaults for the nodes
// note that there are different defaults for group nodes and "normal" nodes
graph.nodeDefaults.style = new ShapeNodeStyle({ fill: Color.DARK_ORANGE })
graph.nodeDefaults.labels.layoutParameter = InteriorNodeLabelModel.CENTER
graph.groupNodeDefaults.style = new GroupNodeStyle({
tabFill: 'lightseagreen',
tabPadding: 20
})
graph.groupNodeDefaults.labels.layoutParameter =
new GroupNodeLabelModel().createTabParameter()
This allows you to visually distinguish group nodes from regular nodes, making the graph easier to understand.
Keeping this in mind, we can try to create the sample graph which is shown in Grouping Hierarchy. The following example shows how the aforementioned createNode and createGroupNode can be used to build a group node with two children:
// Create a group node at the root
const independentGroup = graph.createGroupNode(
null,
new Rect(180, -20, 120, 60)
)
graph.addLabel(independentGroup, 'Independent Group')
// Create two nodes as children of independentGroup
const n4 = graph.createNode(independentGroup, new Rect(200, 0, 30, 30))
graph.addLabel(n4, '4')
const n5 = graph.createNode(independentGroup, new Rect(250, 0, 30, 30))
graph.addLabel(n5, '5')
A group node is defined as a node which is capable of having children. It does not necessarily have to have children, though. You can query and also set whether a node is a group node using the following methods.
- isGroupNode(node: INode): boolean
- Determines whether a node is a normal node or a group node.
- setIsGroupNode(node: INode, isGroupNode: boolean): void
- Converts a normal node into a group node and vice versa.
Note that turning a group node into a normal node will fail if the node has child nodes. The other way around, if you set a normal node as a parent of another node, it will automatically become a group node.
You can still change the parent of a node after creation. Setting a node’s parent to null
will place the node
top-level into the graph. IGraph supports setting the parent for one or more nodes at the
same time:
- setParent(node: INode, parent: INode): void
- Sets the given node as the new parent of the child node. Setting
null
as the parent will re-parent the node to the root, i.e., as not in a group. - groupNodes(parent: INode, children: IEnumerable<INode>): void
- Sets the given node as the parent for all nodes in the given
Enumerable
. - groupNodes(parent: INode, children: IEnumerable<INode>): void
- groupNodes(children: IEnumerable<INode>, style: INodeStyle, tag: Object): INode
- Creates a new group node and sets it as the parent for the given nodes.
Grouping nodes doesn’t affect the group or child node’s layout. You have to adjust the layout explicitly as shown below. Automatic Layout Algorithms also automatically adjust the size of all group nodes in the graph to encompass their children.
With this knowledge, we can build the other half of the sample graph shown in Grouping Hierarchy:
// Create two nodes at top level
const n1 = graph.createNode(new Rect(0, 0, 30, 30))
graph.addLabel(n1, '1')
const n2 = graph.createNode(new Rect(50, 0, 30, 30))
graph.addLabel(n2, '2')
// Create a group node
const innerGroup = graph.createGroupNode(null, new Rect(-10, -20, 100, 60))
graph.addLabel(innerGroup, 'Inner Group')
// set innerGroup as parent of n1 and n2
graph.setParent(n1, innerGroup)
graph.setParent(n2, innerGroup)
// create a node at root ("top level")
const n3 = graph.createNode(new Rect(120, 0, 30, 30))
graph.addLabel(n3, '3')
// Now group innerGroup and n3:
const outerGroup = graph.groupNodes([innerGroup, n3])
graph.addLabel(outerGroup, 'Outer Group')
// note that the group is created at the nearest common ancestor (!)
// therefore, inner_group and n3 are grouped, actually
graph.getChildren(outerGroup) // innerGroup, n3
// grouping nodes doesn't adjust the size automatically
// adjust outerGroup's size to enclose its children
graph.adjustGroupNodeLayout(outerGroup)
Removing a group node removes the group node but not its children. Instead, the children move one level up in the hierarchy by automatically setting their parent to the removed node’s parent.

Traversing the Hierarchy
IGraph provides methods to get the children and parents of a (group) node: getParent and getChildren. A more complex analysis of the node hierarchy is supported by class GroupingSupport, which you can obtain for an IGraph with groupingSupport.
The following table lists the hierarchy-related methods of IGraph and GroupingSupport and shows their output based on the example above:
Method | Description | Example usage | Example output |
---|---|---|---|
IGraph | |||
null if the node is not in a group. | getParent(n2) | Inner Group | |
getParent(outerGroup) | null | ||
getChildren(outerGroup) | Inner Group , 3 | ||
getChildren(null) | Outer Group , Independent Group | ||
GroupingSupport | |||
getDescendants(outerGroup) | 3 , Inner Group , 2 , 1 . | ||
getDescendantsBottomUp(outerGroup) | 1 , 2 , Inner Group , 3 . | ||
isDescendant(n1, outerGroup) | true | ||
isDescendant(n4, outerGroup) | false | ||
getAncestors(n2) | n2 , innerGroup , outerGroup | ||
null if there is none. | getNearestCommonAncestor(n1, n2) | Inner Group | |
getNearestCommonAncestor(n1, n2, n3) | Outer Group | ||
getNearestCommonAncestor(n1, n2, n3, n4) | null |
Adjusting the Size of a Group Node
If the grouping hierarchy is changed by user interaction (for example, if the user drags a node into a group node), the size of the group node is automatically adjusted.
Using the IGraph APIs to add or remove child nodes or to create a group node for a given set of children does not automatically adjust the group node’s layout. You must manually adjust the group node size after changing its children.
- adjustGroupNodeLayout(groupNode: INode): void
- Adjusts the size of the specified group node to enclose all child nodes with minimal space. This method does not affect the group node’s ancestors.
Note that adjustGroupNodeLayout does not adjust the ancestors of the specified group node. You can easily accomplish this by iterating over the nodes returned by getAncestors:
graph.groupingSupport.getAncestors(groupNode).forEach((node) => {
graph.adjustGroupNodeLayout(node)
})
A further extension to the grouping concept is the concept of folding: expanding (opening) and collapsing (closing) group nodes. Folding is also supported by yFiles for HTML; see chapter Folding for more details.