Model class

Models hold the essential data of a diagram, describing the basic entities and their properties and relationships without specifying the appearance and behavior of the Nodes and Links and Groups that represent them visually. Models tend to hold only relatively simple data, making them easy to persist by serialization as JSON or XML formatted text.

Models hold simple data objects, not Parts such as Nodes or Links. Node data is normally represented in a Diagram by instances of Node, but they could be represented by simple Parts or by Groups. A Diagram constructs Parts for its Diagram#model's data by copying templates. Templates are Panels of GraphObjects that get some property values from the model data, accessible via the Panel#data property, using data Binding. See Using Models and Data Binding for an introduction.

This Model class only supports holding an array of node data and interpreting properties on that data to be able to refer to them using unique key values. To support simple tree-structured graphs, use a TreeModel, which inherits from this class. To support links and grouping, use a GraphLinksModel.

Each node data object is assumed to have a unique key value. The #nodeKeyProperty property names the property on the node data whose value is the unique key for that node data object. The default value for this property is "key". You should not have a TwoWay data binding on the node key property, because that might cause the property value to be set to a duplicate key value.

The key values must be either strings or numbers or undefined. If the key is undefined, or if there are duplicate key values, the model will automatically try to assign a new unique key value. Caution: if your keys are numbers, do not try to use string representations of those numbers as keys. Conversely, if your keys are strings that happen to have number syntax, do not try to use those number values. Sometimes JavaScript will automatically convert from string to number or vice-versa, but sometimes it won't.

For example, one can define a graph consisting of just two nodes:

 model.nodeDataArray = [
   { key: "Alpha" },
   { key: "Beta" }
 ];

This model cannot detect the modification of the #nodeDataArray array or the modification of any node data object. If you want to add or remove node data from the #nodeDataArray, call the #addNodeData or #removeNodeData methods.

If you want to modify a node data object, it depends on whether the property you want to change is a structural property that the model needs to know about, or whether it is a property that is only used for data binding or other application-specific purposes.

For the former case, call the appropriate method, such as #setKeyForNodeData, #setCategoryForNodeData, GraphLinksModel#setToKeyForLinkData, or GraphLinksModel#setGroupKeyForNodeData. These methods have names that start with "set", "add", "insert", or "remove".

For the latter case, when setting an application-specific property, typically for data binding, and to support undo/redo, call #setDataProperty.

The #copyNodeData method can be called to make a shallow copy of a node data object. However, if some of those property values are Arrays that want not to be shared but to be copied, you can set #copiesArrays to true. This is typically very useful when dealing with data bound item arrays. Furthermore if the items in those copied Arrays are in fact Objects that need to be copied, you can also set #copiesArrayObjects to true, causing a copied Array to refer to newly shallow-copied objects of the original array.

Each model raises ChangedEvents that you can follow by registering a listener via #addChangedListener. Read more at the Introduction page: Changed Events.

Each model comes with its own UndoManager that is initially not enabled. You will need to set UndoManager#isEnabled to true in order for the UndoManager to record model changes and for your users to perform undo and redo.

You can temporarily turn off the recording of changes by setting #skipsUndoManager to true. A number of places within the system do that routinely in order to avoid recording temporary changes, so be sure to remember the original value beforehand and restore it afterwards. Note that in a ChangedEvent listener you may want to ignore events that happen when #skipsUndoManager is true.

One normally saves a diagram by just saving its model. If you can use JSON-formatted text, this is easy to do -- just call #toJson to get the string representation of the model, and save that string. Load the diagram by replacing the Diagram#model with one created by calling the static function Model.fromJson:

  myDiagram.model = go.Model.fromJson(loadedString);

Note that JSON and other textual data formats cannot faithfully store all JavaScript functions. #toJson and Model.fromJson do not try to save and load functional property values. You should arrange that all such functions, including event handlers, are established by your app. #toJson and Model.fromJson also cannot handle circular references; any sharing of references will be lost too. They also skip properties that are not enumerable, those whose names start with an underscore, and those whose values are undefined.

Note that models also do not store the templates used by diagrams, nor any transient or temporary parts such as Adornments, nor any tools, nor any UndoManager state, nor any event listeners. These objects and all other properties of diagrams must be established by your app.

You can add any number of properties to the #modelData object, which is serialized and deserialized into JSON just like any other model data for nodes or links. However #modelData is associated with the model as a whole and does not depend on the existence of any node data or link data.

It is also easy to save the changes that were recorded in the most recent transaction. Call #toIncrementalJson to generate a JSON-format string that holds the current state of modified data plus the keys of inserted or removed data. That method requires as an argument a ChangedEvent that represents a transaction that completed or an undo or a redo that just finished.

It is also possible to use such "incremental" JSON to modify an existing model. Call #applyIncrementalJson, giving it a string generated by #toIncrementalJson, to modify this model by making all of the changes recorded in the JSON text. Note how this method is a regular instance method, whereas Model.fromJson is a static function.

Implementers
Available Extensions
Annotations
  • @JS()
  • @staticInterop

Constructors

Model.$1()
factory
Model.$2([dynamic init])
factory
Model.$3([Array<Object>? nodedataarray, dynamic init])
factory

Properties

hashCode int
The hash code for this object.
no setterinherited
runtimeType Type
A representation of the runtime type of the object.
no setterinherited

Methods

noSuchMethod(Invocation invocation) → dynamic
Invoked when a nonexistent method or property is accessed.
inherited
toString() String
A string representation of this object.
inherited

Operators

operator ==(Object other) bool
The equality operator.
inherited

Static Methods

fromJson(Object s, [Model? model]) Model
This static function parses a string in JSON format that was written by Model#toJson, and then constructs, initializes, and returns a model with that information.