documentationfor yFiles for HTML 3.0.0.1

GraphBuilder

The GraphBuilder creates graphs from separate node and edge collections. An edge data item specifies its source and target nodes by providing their unique node IDs.

Example node and edge data items may look like this:

 interface NodeType {
  nodeId: string
}

interface EdgeType {
  sourceNodeId: string
  targetNodeId: string
}

Each node data item has a unique nodeId property that is referenced by the sourceNodeId and targetNodeId properties on edge data items.

To bind the node data to the graph builder, create a node source:

const graphBuilder = new GraphBuilder()

const nodesData = getNodeData()
const nodesSource = graphBuilder.createNodesSource(
  nodesData, // node data
  (nodeDataItem) => nodeDataItem.nodeId // node id provider
)

createNodesSource<TDataItem> creates a new NodesSource<TDataItem> that is bound to the passed nodesData and its data item type. The passed idProvider takes a data item and returns the nodeId that is used to identify a node when creating edges and during updateGraph. You can use the node data item itself as the node ID.

To bind the edge data to the graph builder, create an edge source:

const edgesData = getEdgesData()
const edgesSource = graphBuilder.createEdgesSource(
  edgesData,
  (edgeDataItem) => edgeDataItem.sourceNodeId,
  (edgeDataItem) => edgeDataItem.targetNodeId
)

createEdgesSource<TDataItem> creates a new EdgesSource<TDataItem> that is bound to the passed edgesData and its data item type. The passed sourceIdProvider and targetIdProvider define the ID of the source and target node data items for each edge data item.

The GraphBuilder constructor alternatively allows defining all sources in a single configuration object:

const graphBuilder = new GraphBuilder({
  nodes: [
    {
      data: getNodeData(),
      id: 'nodeId'
    }
  ],
  edges: [
    {
      data: getEdgesData(),
      sourceId: 'sourceNodeId',
      targetId: 'targetNodeId'
    }
  ]
})

This feature is not available for the other graph builders.

You can create multiple node and edge sources from different data collections and with different styles. Edges can connect nodes from different nodes sources.

It is also possible to add group nodes from group data, as explained below.

The visual appearance, locations, and labels of the nodes and edges can be customized via the NodeCreator<TDataItem> and EdgeCreator<TDataItem>, as explained in the Configuring the Visual Appearance of Graph Items section.

After the sources and creators have been configured, a graph can be created and later updated if the business data has changed.

Grouping

GraphBuilder provides different ways to define Grouping Nodes. First, it supports defining a node’s parent via the parentIdProvider of NodesSource<TDataItem>, as shown in the common section.

Other than the other builders, GraphBuilder allows defining dedicated sources for group nodes via createGroupNodesSource<TDataItem>. Nodes defined here are automatically created as group nodes, and their defaults are the graph’s groupNodeDefaults.

const graphBuilder = new GraphBuilder()
const groupNodesSource = graphBuilder.createGroupNodesSource(
  getGroupNodeData(),
  (data) => data.nodeId
)
const nodesSource = graphBuilder.createNodesSource(
  getItemData(),
  (data) => data.nodeId
)
nodesSource.parentIdProvider = (data) => data.parentId

Another common use case is when the data itself is organized hierarchically; that is, the data object for a node has a collection of data objects representing its child nodes. createChildNodesSource<TChildDataItem> can read such objects and create a hierarchy where the nodes are created as child nodes of the current node:

const graphBuilder = new GraphBuilder()
const nodesSource = graphBuilder.createNodesSource(
  nodeData,
  (data) => data.nodeId
)
nodesSource.createChildNodesSource(
  (data) => data.children,
  (data) => data.nodeId
)

Children can define children of their own. Adding the child nodes source itself as a child nodes source allows traversing the data recursively:

const graphBuilder = new GraphBuilder()
const nodesSource = graphBuilder.createNodesSource(
  nodeData,
  (data) => data.nodeId
)
const childNodesSource = nodesSource.createChildNodesSource(
  (data) => data.children,
  (data) => data.nodeId
)
// parse the children recursively
childNodesSource.addChildNodesSource(
  (data) => data.children,
  childNodesSource
)

A node can also define its parent node or even ancestors using another nodes source with createParentNodesSource<TParentDataItem>. If defined this way, a parent group node is automatically created using the provided NodeCreator<TDataItem>. If a node with the same ID already exists, though, that node will be set as the parent instead of creating a new node.

const graphBuilder = new GraphBuilder()
const nodesSource = graphBuilder.createNodesSource(
  nodeData,
  (data) => data.nodeId
)
// create a nodes source for the node's parent
const parentNodesSource = nodesSource.createParentNodesSource(
  (data) => data.parentData,
  (data) => data.nodeId
)
// configure the node creator to style the parent nodes
parentNodesSource.nodeCreator.defaults.style = new GroupNodeStyle()

Defining a parent nodes source can also define a complex hierarchy. The following example parses a string representing an IP address to create a grouping by its octets.

const graphBuilder = new GraphBuilder()
const nodesSource = graphBuilder.createNodesSource(
  networkObjects,
  (data) => data.nodeId
)
// Parent data provider: parse the IP into smaller units by removing the last octet
const parentDataProvider = (ip) => {
  const separator = ip?.lastIndexOf('.')
  if (separator != -1) {
    return ip ? ip.substring(0, separator) : null
  }

  return null
}
const parentNodesSource = nodesSource.createParentNodesSource((data) =>
  parentDataProvider(data.IP)
)
//Enable recursive ascent for the new parent source
parentNodesSource.addParentNodesSource(
  parentDataProvider,
  parentNodesSource
)

const graphBuilder = new GraphBuilder()
const nodesSource = graphBuilder.createNodesSource(
  networkObjects,
  (data) => data.nodeId
)
// Parent data provider: parse the IP into smaller units by removing the last octet
const parentDataProvider = (ip: string | null) => {
  const separator = ip?.lastIndexOf('.')
  if (separator != -1) {
    return ip ? ip.substring(0, separator) : null
  }

  return null
}
const parentNodesSource = nodesSource.createParentNodesSource((data) =>
  parentDataProvider(data.IP)
)
//Enable recursive ascent for the new parent source
parentNodesSource.addParentNodesSource(
  parentDataProvider,
  parentNodesSource
)

The next example shows a flat arrangement of data used to create a complex hierarchy. In that example, a data item represents an office with its city and country stored in separate properties.

// sample data structure

class Office {
  ID = null
  city = null
  country = null
}

// sample data structure

class Office {
  ID: string | null = null
  city: string | null = null
  country: string | null = null
  // other properties...
}

const graphBuilder = new GraphBuilder()
const officeSource = graphBuilder.createNodesSource(
  offices,
  (data) => data.ID
)
// the offices are grouped by their cities
const citySource = officeSource.createParentNodesSource(
  (data) => data,
  (data) => data.city
)
// the cities are grouped by their countries
const countrySource = citySource.createParentNodesSource(
  (data) => data,
  (data) => data.country
)

Note that the data item itself is passed as the data item for the nodes and its ancestors. To distinguish properly between the different nodes, specifying an ID provider is mandatory.

Tutorial Demo Code

The demos Simple Graph Builder and Graph Builder show how to use a GraphBuilder to build a graph from user data. See the multistep GraphBuilder Tutorial for in-depth explanations of the various concepts.