Skip to main content
  1. Index

Using Models and Templates

In GoJS, appearance is separated from data. You declare appearance in Diagram templates and data in a Model. Data bindings on the templates connect the two.

A Model is a collection of data that holds only the essential information for each node and each link. Models, not Diagrams, are what you load and then save after editing. A template is a Part that can be copied; there are different templates for Nodes, Links, and Groups.

A Diagram already has very simple default templates for Nodes and Links. To customize the appearance of the nodes in your diagram, you replace the defaults by setting Diagram.nodeTemplate and Diagram.linkTemplate.

A GraphLinksModel holds GraphLinksModel.nodeDataArray and GraphLinksModel.linkDataArray. Setting Diagram.model creates Nodes and Links for all the data. Each node data must have a unique key value so that references can be resolved.

Node and link data can be any JavaScript object. GoJS expects certain properties: "key" on node data, "from" and "to" on link data, and optionally "category". You can customize these names via model properties like Model.nodeKeyProperty.

A simple template and model example:

Note how the nodes look identical! Typically we want to parameterize their appearance conditional on the Model data. We can achieve that parameterization by using data binding.

Parameterizing Nodes using data binding

A data binding is a declaration that the value of the property of one object should be used to set the value of a property of another object.

In this case, we want to make sure that the TextBlock.text property gets the "key" value of the corresponding node data. And we want to make sure that the Shape.fill property gets set to the color/brush given by the "color" property value of the corresponding node data.

We can declare such data-bindings by creating Binding objects and associating them with the target GraphObject. Programmatically you do this by calling GraphObject.bind.

You can easily add more node and link data to build bigger diagrams. And you can easily change the appearance of all of the nodes without modifying the data.

Notice that the value of Shape.fill in the template above gets a value twice. First it is set to "white". Then the binding sets it to whatever value the node data's color property has. It may be useful to be able to specify an initial value that remains in case the node data does not have a color property. The third node has no color or text specified so it gets the defaults.

At this point we can also be a bit more precise about what a template is. A template is a Part that may have some data Bindings and that is not itself in a diagram but may be copied to create parts that are added to a diagram.

Template Definitions

The implementations of all predefined templates are provided in Templates.js in the Extensions directory. You may wish to copy and adapt these definitions when creating your own templates. More likely you will want to adapt one of the more complex templates from a sample, or build your own from scratch.

Kinds of Models

A model is a way of interpreting a collection of data objects as an abstract graph with various kinds of relationships determined by data properties and the assumptions that the model makes. The simplest kind of model, Model, can only hold "parts" without any relationships between them -- no links or groups. But that model class acts as the base class for other kinds of models.

GraphLinksModel

GraphLinksModel is the most general model. It uses separate link data objects for each Link, allowing reflexive links, duplicate links, and cycles (though you can restrict these via Diagram.validCycle). It also supports ports (multiple connection points per node) and group membership.

TreeModel

TreeModel is simpler, only supporting link relationships that form a tree-structured graph. There is no separate link data, so there is no "linkDataArray". There is no separate link data array. Instead, child nodes reference their parent by key via a "parent" property: Each Link is still data bound, but the link's data is the child node data.

A tree-structured graph doesn't require TreeModel, you can use GraphLinksModel with separate link data if that fits your data better, or if you need features like Groups.

Other pages such as Trees discuss tree-oriented features of GoJS in more detail.

References to Nodes

Although the identity of a node is the node's data object in the model, references to nodes are not "pointers" to those objects. Instead, references are always by the "key" of the node data. (The property need not be named "key" -- see Model.nodeKeyProperty.) Using keys instead of direct references to data objects makes it easier to read and write models, especially by Model.toJson and Model.fromJson, and to debug them in memory. Thus Links are defined by data using keys, and Group membership is determined by data using keys:

js
js

Modifying Models

To add or remove nodes, call Model.addNodeData and Model.removeNodeData. Use Model.findNodeDataForKey to look up node data by key. You may also call Model.copyNodeData to make a copy of a node data object that you can then modify and pass to Model.addNodeData.

Important: Do not directly mutate the Model.nodeDataArray — GoJS won't be notified. Similarly, do not directly set properties on node data objects — bindings won't update.

js

Use Model.set instead:

js

These model methods are only required once data is part of the Model. When initially building your arrays before setting Model.nodeDataArray, you can set properties directly.

Saving and Loading Models

Call Model.toJson to serialize a model to a JSON string, and Model.fromJson to deserialize one. Data properties are saved as long as they are enumerable, don't start with an underscore, and have JSON-compatible values (numbers, strings, Arrays, or plain Objects).

To associate metadata with the model itself (not any particular node), use the Model.modelData object, which is also included in JSON serialization.

Externally Modified Data

In some software architectures it might not be possible to insist that all data changes go through Model methods. In such cases it is possible to call Diagram.updateAllRelationshipsFromData and Diagram.updateAllTargetBindings.

However, please note that doing so will prevent the UndoManager from properly recording state changes. There would be no way for the UndoManager to know what had been the previous values of properties. Furthermore it makes it hard to have more than one Diagram showing the Model.

Immutable Data

In some software architectures it is customary to have "models" consist of immutable (unmodifiable) data. However, as the GoJS diagram is modified, its model data will be modified, so you cannot use that immutable data in the model. You could make a copy of all of the immutable data and then replace the Diagram.model whenever the data has changed outside of the diagram/model. But that would cause old Nodes and Links to be re-created, and that would be unworkably expensive in time and space when the model is large.

If you do have immutable model data, you can update the existing Model and thus its Diagrams by calling the Model.mergeNodeDataArray and GraphLinksModel.mergeLinkDataArray methods. This will be much more efficient than replacing the Model.nodeDataArray and GraphLinksModel.linkDataArray Arrays each time, because it will preserve the existing Nodes and Links if possible.

Note that this scheme depends on maintaining the "key"s for all of the node data and for all of the link data. That happens automatically for all nodes, but for GraphLinksModels, it means setting GraphLinksModel.linkKeyProperty to the name of the property on the link data that you want to use to remember the key value.

After each diagram transaction some of the model data may have changed. But you cannot share references to that modified data with the rest of the software that is expecting immutable data. Instead you can call Model.toIncrementalData which will provide copies of the modified data. That data can then be used to update the rest of the app's state. Read more about this at Using GoJS with React and the gojs-react package, which provides generic Diagram components that you can use in your app using React.