Class Model

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 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.

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.

Constructor Summary Details

Returns Name Description
 
Model(nodedataarray)
You probably don't want to call this constructor, because this class does not support links (relationships between nodes) or groups (nodes and links and subgraphs as nodes): instead, create instances of a subclass such as GraphLinksModel or TreeModel. More...
Parameters:
{Array=} nodedataarray
an optional Array containing JavaScript objects to be represented by Parts.

Properties Summary Details

Returns Name Description
{boolean}
copiesArrayObjects 1.5
Gets or sets whether the default behavior for copyNodeData when copying Arrays also copies array items that are Objects. More... This only covers copying Objects that are items in Arrays that are copied when copiesArrays is true. Copying an Object when this property is true also recursively copies any Arrays that are property values. It also assumes that the object's constructor can be called with no arguments.

The default value is false. This property does not affect any behavior when the value of copyNodeDataFunction has been set to a function. This property has no effect unless copiesArrays is true.

Caution: if you want a copied data object to share some references but not others, you will need to provide your own copying function as copyNodeDataFunction rather than setting this property and copiesArrays to true.

Warning: there should not be any cyclical references within the model data.

See also:
{boolean}
copiesArrays 1.5
Gets or sets whether the default behavior for copyNodeData makes copies of property values that are Arrays. More... This only copies Arrays that are top-level property values in node data objects, not for Arrays that are in nested objects. Copying Arrays will also copy any array items that are Objects when copiesArrayObjects is true.

The default value is false. It is commonplace to set copiesArrayObjects to true when setting this property to true. This property does not affect any behavior when the value of copyNodeDataFunction has been set to a function.

Caution: if you want a copied data object to share some Arrays but not others, you will need to provide your own copying function as copyNodeDataFunction rather than setting this property to true.

Warning: there should not be any cyclical references within the model data.

See also:
{function(Object, Model):Object | null}
copyNodeDataFunction
Gets or sets a function that makes a copy of a node data object. More...

You may need to set this property in order to ensure that a copied Node is bound to data that does not share certain data structures between the original node data and the copied node data. This property value may be null in order to cause copyNodeData to make a shallow copy of a JavaScript Object. The default value is null.

It is common to implement a copying function when the node data has an Array of data and that Array needs to be copied rather than shared. Often the objects that are in the Array also need to be copied.

{string}
dataFormat
Gets or sets the name of the format of the diagram data. More... The default value is the empty string. The value must not be null. Use different values to prevent parts from one model to be copy/pasted or drag-and-dropped into another diagram/model.
{boolean}
isReadOnly
Gets or sets whether this model may be modified, such as adding nodes. More... By default this value is false. Setting the nodeDataArray to something that is not a true Array of Objects will cause this to be set to true.

Model methods and property setters do not heed this property. It is up to code that uses a model to check this property when it might want to prevent changes to the model.

{function(Model, Object):(string|number) | null}
makeUniqueKeyFunction
Gets or sets a function that returns a unique id number or string for a node data object. More... This function is called by makeNodeDataKeyUnique when a node data object is added to the model, either as part of a new nodeDataArray or by a call to addNodeData, to make sure the value of getKeyForNodeData is unique within the model.

The value may be null in order to cause makeNodeDataKeyUnique behave in the standard manner. (The default value is null.) You may want to supply a function here in order to make sure all of the automatically generated keys are in a particular format. Setting this property after setting nodeDataArray has no real effect until there is a call to addNodeData.

If a node data object is already in the model and you want to change its key value, call setKeyForNodeData with a new and unique key.

{Object}
modelData
Gets a JavaScript Object that can hold programmer-defined property values for the model as a whole, rather than just for one node or one link. More...

By default this an object with no properties. Any properties that you add to this object will be written out by toJson and will be restored by Model.fromJson, if the following conditions are true:

  • the property is enumerable and its name does not start with an underscore ('_')
  • the property value is not undefined and is not a function
  • the model knows how to convert the property value to JSON format
  • property values that are Objects or Arrays form a tree structure -- no shared or cyclical references
Most object classes cannot be serialized into JSON without special knowledge and processing at both ends. The toJson and Model.fromJson methods automatically do such processing for numbers that are NaN and for objects that are of class Point, Size, Rect, Margin, Spot, Brush (but not for brush patterns), and for Geometry.

At the current time one cannot use this object as a Binding source, and one cannot have a Diagram as a binding target. Calling setDataProperty will work to change a property value, but there are no target bindings in any Diagrams to be updated. Because the binding mechanism is unavailable for this object, we recommend that when you want to save a model that you explicitly set properties on this object just before calling toJson. When loading a model, call Model.fromJson and explicitly get the properties that you want to set on a Diagram.

{string}
name
Gets or sets the name of this model. More... The initial name is an empty string. The value must not be null.
{string|function(Object,string=):string}
nodeCategoryProperty
Gets or sets the name of the node data property that returns a string naming that data's category, or a function that takes a node data object and returns the category name; the default value is the name 'category'. More... This is used by the diagram to distinguish between different kinds of nodes. The name must not be null. If the value is an empty string, getCategoryForNodeData will return an empty string for all node data objects.

See also:
{Array.<Object>}
nodeDataArray
Gets or sets the array of node data objects that correspond to Nodes, Groups, or non-Link Parts in the Diagram. More... The initial value is an empty Array.

For each Object in the Array, getKeyForNodeData should return a number or string uniquely identifying the node data within the model. If it returns undefined, this calls makeNodeDataKeyUnique, to make sure the node data has a unique key. These key values may be used by other objects to refer to that particular node data object. If more than one node data object has the same key, there may be some confusion about which object to reference.

If you want to use a custom data property for holding the unique key value on a node data object, you should set nodeKeyProperty before you set this nodeDataArray property.

Adding or removing data from this Array will not notify this model or the diagram that there are any new nodes or that any nodes have been deleted. Instead you should call addNodeData or removeNodeData.

{string|function(Object,(string|number)=):(string|number)}
nodeKeyProperty
Gets or sets the name of the data property that returns a unique id number or string for each node data object, or a function taking a node data object and returning the key value; the default value is the name 'key'. More... The name must not be null or the empty string. You must set this property before assigning the nodeDataArray.

See also:
{boolean}
skipsUndoManager
Gets or sets whether ChangedEvents are not recorded by the UndoManager. More... The initial and normal value is false.

When this property is true, changing the Model or any data object does not call UndoManager.handleChanged. Even when this property is true, transactions (such as calls to startTransaction) and undo/redo (such as calls to CommandHandler.undo) are still delegated to the undoManager.

You should set this to true only temporarily, and you should remember its previous value before setting this to true. When finishing the period for which you want the UndoManager to be disabled, do not blindly set this property to false. You should set this back to the value it had before you set it to true. For more permanent disabling of the UndoManager, set UndoManager.isEnabled to false.

This property is also set when setting Diagram.skipsUndoManager. Setting this property does not raise a ChangedEvent.

undoManager
Gets or sets the UndoManager for this Model. More...

The default UndoManager has its UndoManager.isEnabled property set to false. If you want users to undo and redo, you should set that property to true once you have initialized the Diagram or its Model.

This property setter does not raise a ChangedEvent.

Method Summary Details

Returns Name Description
addArrayItem(arr, val) 1.1
Add an item at the end of a data array that may be data bound by a Panel as its Panel.itemArray, in a manner that can be undone/redone and that automatically updates any bindings. More...

This also calls raiseChangedEvent to notify all listeners about the ChangedEvent.Insert.

If you want to add a new node or part to the diagram, call addNodeData.

See also:
Parameters:
{Array} arr
an Array that is the value of some Panel's Panel.itemArray.
{*} val
the new value to be pushed onto the array.
addChangedListener(listener)
Register an event handler that is called when there is a ChangedEvent. More...

This registration does not raise a ChangedEvent.

Parameters:
{function(ChangedEvent)} listener
a function that takes a ChangedEvent as its argument.
addNodeData(nodedata)
When you want to add a node or group to the diagram, call this method with a new data object. More... This will add that data to the nodeDataArray and notify all listeners that a new node data object has been inserted into the collection.

To remove a node from the diagram, you can remove its data object by calling removeNodeData.

To add or remove an object or value from an item array, call insertArrayItem or removeArrayItem.

Parameters:
{Object} nodedata
a JavaScript object representing a node, group, or non-link.
addNodeDataCollection(coll) 1.3
Add to this model all of the node data held in an Array or in an Iterable of node data objects. More...
Parameters:
{Iterable.|Array.} coll
a collection of node data objects to add to the nodeDataArray
clear()
Clear out all references to any model data. More... This also clears out the UndoManager, so this operation is not undoable. This method is called by Diagram.clear; it does not notify any Diagrams or other listeners.

Instead of calling this method, you may prefer to set nodeDataArray to an empty JavaScript Array. If this model is a GraphLinksModel, you would also want to set GraphLinksModel.linkDataArray to a separate empty JavaScript Array.

{boolean}
commitTransaction(tname)
Commit the changes of the current transaction. More... This just calls UndoManager.commitTransaction.
Parameters:
{string=} tname
a descriptive name for the transaction.
Returns:
{boolean} the value returned by UndoManager.commitTransaction.
{boolean}
containsNodeData(nodedata)
Decide if a given node data object is in this model, using reference equality. More...

If you do not have a reference to the particular data object that is in the nodeDataArray, you may need to search for it by iterating through that Array, or by finding the desired Node or simple #Part in a Diagram and getting that node's Panel.data, or most likely by calling findNodeDataForKey.

Parameters:
{Object} nodedata
a JavaScript object representing a node, group, or non-link.
Returns:
{boolean} true if it is a node data object in this model; false otherwise.
{Object}
copyNodeData(nodedata)
Make a copy of a node data object. More... This uses the value of copyNodeDataFunction to actually perform the copy, unless that property is null. When it is null the default behavior is to just make a shallow copy of the JavaScript Object.

However when copiesArrays is true, this will make a copy of property values that are JavaScript Arrays. This is useful when you do not want the Arrays to be shared between the node data objects. Note that if you want to copy some property values that are Arrays but not other properties that are Arrays, you cannot use copiesArrays but must implement your own copyNodeDataFunction.

In addition when copiesArrayObjects is true, if items in the Array being copied are JavaScript Objects, those objects are copied, recursively. This is useful when the items in copied Arrays are themselves objects that need to be copied. Note that if you want to share references to some but not all of the objects in copied Arrays you cannot use copiesArrayObjects but must implement your own copyNodeDataFunction.

This does not modify the model -- the returned data object is not added to this model. This assumes that the data's constructor can be called with no arguments.

Models should not have any references to Diagrams or GraphObjects or Tools or Layouts or other objects that form a Diagram.

Warning: there should not be any cyclical references within the model data, unless you either do not turn on copiesArrays or copiesArrayObjects or unless you have supplied your own copyNodeDataFunction that can handle cyclical references.

See also:
Parameters:
{Object} nodedata
a JavaScript object representing a node, group, or non-link.
Returns:
{Object}
{Object}
findNodeDataForKey(key)
Given a number or string, find the node data object in this model that uses the given value as its unique key. More...

See also:
Parameters:
{(string|number|undefined)} key
a string or a number.
Returns:
{Object} null if the key is not present in the model, or if the key is null or undefined or not a string or number.
<static> {Model}
Model.fromJson(s, model)
This static function parses a string in JSON format and constructs, initializes, and returns a model. More...

Note that properties with values that are functions are not written out by toJson, so reading in such a model will require constructing such a model, initializing its functional property values, and explicitly passing it in as the second argument.

Parameters:
{string|Object} s
a String in JSON format containing all of the persistent properties of the model, or an Object already read from JSON text.
{Model=} model
an optional model to be modified; if not supplied, it constructs and returns a new model whose name is specified by the "class" property.
Returns:
{Model} the supplied or created model loaded with data from the given string.
{string}
getCategoryForNodeData(nodedata)
Find the category of a given node data, a string naming the node template or group template or part template that the Diagram should use to represent the node data. More...

See also:
Parameters:
{Object} nodedata
a JavaScript object representing a node, group, or non-link.
Returns:
{string}
{string|number|undefined}
getKeyForNodeData(nodedata)
Given a node data object return its unique key: a number or a string. More... This returns undefined if there is no key value.

It is possible to change the key for a node data object by calling setKeyForNodeData.

See also:
Parameters:
{Object} nodedata
a JavaScript object representing a node, group, or non-link.
Returns:
{string|number|undefined}
insertArrayItem(arr, idx, val)
Add an item to a data array that may be data bound by a Panel as its Panel.itemArray, given a new data value and the index at which to insert the new value, in a manner that can be undone/redone and that automatically updates any bindings. More...

This also calls raiseChangedEvent to notify all listeners about the ChangedEvent.Insert.

If you want to add a new node or part to the diagram, call addNodeData.

See also:
Parameters:
{Array} arr
an Array that is the value of some Panel's Panel.itemArray.
{number} idx
the zero-based array index where the new value will be inserted; use -1 to push the new value on the end of the array.
{*} val
the new value to be inserted into the array.
makeNodeDataKeyUnique(nodedata)
This method is called when a node data object is added to the model to make sure that getKeyForNodeData returns a unique key value. More...

The key value should be unique within the set of data managed by this model: nodeDataArray. If the key is already in use, this will assign an unused number to the nodeKeyProperty property on the data.

If you want to customize the way in which node data gets a unique key, you can set the makeUniqueKeyFunction functional property.

If the node data object is already in the model and you want to change its key value, call setKeyForNodeData and give it a new unique key value.

Parameters:
{Object} nodedata
a JavaScript object representing a node, group, or non-link.
raiseChangedEvent(change, propertyname, obj, oldval, newval, oldparam, newparam)
Call this method to notify that the model or its objects have changed. More... This constructs a ChangedEvent and calls all Changed listeners.
Parameters:
{EnumValue} change
specifies the general nature of the change; typically the value is ChangedEvent.Property.
{string|function(Object):*} propertyname
names the property that was modified, or a function that takes an Object and returns the property value.
{Object} obj
the object that was modified, typically a GraphObject, Diagram, or a Model.
{*} oldval
the previous or older value.
{*} newval
the next or newer value.
{*=} oldparam
an optional value that helps describe the older value.
{*=} newparam
an optional value that helps describe the newer value.
raiseDataChanged(data, propertyname, oldval, newval, oldparam, newparam)
Call this method to notify about a data property having changed value. More... This constructs a ChangedEvent and calls all Changed listeners.

You should call this method only if the property value actually changed. This method is called by setDataProperty.

Parameters:
{Object} data
the data object whose property changed value.
{string|function(Object):*} propertyname
the name of the property, or a function that takes an Object and returns the property value.
{*} oldval
the previous or old value for the property.
{*} newval
the next or new value for the property.
{*=} oldparam
an optional value additionally describing the old value.
{*=} newparam
an optional value additionally describing the new value.
removeArrayItem(arr, idx)
Remove an item from a data array that may be data bound by a Panel as its Panel.itemArray, given the index at which to remove a data value, in a manner that can be undone/redone and that automatically updates any bindings. More...

This also calls raiseChangedEvent to notify all listeners about the ChangedEvent.Remove.

If you want to remove a node from the diagram, call removeNodeData.

Note that there is no version of this method that takes an item value instead of an index into the array. Because item arrays may hold any JavaScript value, including numbers and strings, there may be duplicate entries with that value in the array. To avoid ambiguity, removing an item from an array requires an index.

See also:
Parameters:
{Array} arr
an Array that is the value of some Panel's Panel.itemArray.
{number=} idx
the zero-based array index of the data item to be removed from the array; if not supplied it will remove the last item of the array.
removeChangedListener(listener)
Unregister an event handler listener. More...

This deregistration does not raise a ChangedEvent.

Parameters:
{function(ChangedEvent)} listener
a function that takes a ChangedEvent as its argument.
removeNodeData(nodedata)
When you want to remove a node or group from the diagram, call this method with an existing data object. More... This will remove that data from the nodeDataArray and notify all listeners that a node data object has been removed from the collection.

If you do not have a reference to the particular data object that is in the nodeDataArray, you may need to search for it by iterating through that Array, or by finding the desired Node or simple #Part in a Diagram and getting that node's Panel.data, or most likely by calling findNodeDataForKey.

Removing a node data from a model does not automatically remove any connected link data from the model. Removing a node data that represents a group does not automatically remove any member node data or link data from the model.

To add a node to the diagram, you can add its data object by calling addNodeData.

To add or remove an object or value from an item array, call insertArrayItem or removeArrayItem.

Parameters:
{Object} nodedata
a JavaScript object representing a node, group, or non-link.
removeNodeDataCollection(coll) 1.3
Remove from this model all of the node data held in an Array or in an Iterable of node data objects. More...
Parameters:
{Iterable.|Array.} coll
a collection of node data objects to remove from the nodeDataArray
{boolean}
rollbackTransaction()
Rollback the current transaction, undoing any recorded changes. More... This just calls UndoManager.rollbackTransaction.
Returns:
{boolean} the value returned by UndoManager.rollbackTransaction.
setCategoryForNodeData(nodedata, cat)
Change the category of a given node data, a string naming the node template or group template or part template that the Diagram should use to represent the node data. More...

Changing the node template for a node data will cause the existing Node, Group, or Part to be replaced with a new Node, Group, or Part created by copying the new node template and applying any data-bindings.

See also:
Parameters:
{Object} nodedata
a JavaScript object representing a node, group, or non-link.
{string} cat
Must not be null.
setDataProperty(data, propname, val)
Change the value of some property of a node data, a link data, or an item data, given a string naming the property and the new value, in a manner that can be undone/redone and that automatically updates any bindings. More...

This gets the old value of the property; if the value is the same as the new value, no side-effects occur.

Parameters:
{Object} data
a JavaScript object representing a Node, Link, Group, simple Part, or item in a Panel.itemArray.
{string} propname
a string that is not null or the empty string.
{*} val
the new value for the property.
setKeyForNodeData(nodedata, key) 1.1
Change the unique key of a given node data that is already in this model. More... The new key value must be unique -- i.e. not in use by another node data object. You can call findNodeDataForKey to check if a proposed new key is already in use.

This operation will check all data objects in the model and replace all references using the old key value with the new one.

If this is called on a node data object that is not (yet) in this model, this unconditionally modifies the property to the new key value.

See also:
Parameters:
{Object} nodedata
a JavaScript object representing a node, group, or non-link.
{string|number} key
{boolean}
startTransaction(tname)
Begin a transaction, where the changes are held by a Transaction object in the UndoManager. More... This just calls UndoManager.startTransaction.
Parameters:
{string=} tname
a descriptive name for the transaction.
Returns:
{boolean} the value returned by UndoManager.startTransaction.
{string}
toJson(classname)
Generate a string representation of the persistent data in this model, in JSON format. More...

Object properties that are not enumerable or whose names start with "_" are not written out.

Functions are not able to be written in JSON format, so any properties that have function values will not be saved in the JSON string.

There must not be any circular references within the model data. Any sharing of object references will be lost in the written JSON.

Most object classes cannot be serialized into JSON without special knowledge and processing at both ends. The toJson and Model.fromJson methods automatically do such processing for numbers that are NaN and for objects that are of class Point, Size, Rect, Margin, Spot, Brush (but not for brush patterns), and for Geometry. However, we recommend that you use Binding converters (static functions named "parse" and "stringify") to represent Points, Sizes, Rects, Margins, Spots, and Geometries as string values in your data, rather than as Objects. This makes the JSON text smaller and simpler and easier to read.

Parameters:
{string=} classname
The optional name of the model class to use in the output; for the standard models, this is their class name prefixed with "go.".
Returns:
{string} a String in JSON format containing all of the persistent properties of the model.
updateTargetBindings(data, srcpropname)
Find a Part corresponding to the given data and call its Panel.updateTargetBindings method, in each Diagram that uses this Model. More...

Caution: setting a data property without calling setDataProperty and then calling this updateTargetBindings method will update GraphObjects that are bound to the property, but such data settings will not be recorded in the UndoManager and therefore will not be undone/redone, causing an inconsistency between the GraphObjects and the part data.

Parameters:
{Object} data
The data object in this model that was modified.
{string=} srcpropname
If not present or the empty string, update all bindings on the target Part or item Panel; otherwise update only those bindings using this source property name.