Binding class

A Binding describes how to automatically set a property on a GraphObject to a value of a property of data in the model. The target property name and the data source property name are strings. All name matching is case-sensitive.

Register bindings by calling GraphObject#bind with a new Binding. Existing bindings become read-only, and no new bindings may be added, when a template (a Part) is copied. Bindings will be shared by all copies of the template's GraphObjects.

For example, your node data might be like:

{ key: 23, say: "hello!" }

Your simple node template might be like:

  var template = new go.Node(go.Panel.Auto);
  // . . . define the rest of the Node's visual tree . . .
  var txt = new go.TextBlock();
  txt.bind(new go.Binding("text", "say"));
  template.add(txt);
  myDiagram.nodeTemplate = template;

Using GraphObject.make it might look like:

  var $ = go.GraphObject.make;
  myDiagram.nodeTemplate =
    $(go.Node, "Auto",
      . . .
      $(go.TextBlock, new go.Binding("text", "say"))
    )

The data binding causes the TextBlock#text property of the TextBlock to be set to the value of the data's "say" property. If the value of the "say" property of a particular data object is undefined, the binding is not evaluated: the target property is not set. If there is an error with the binding, you may see a message in the console log. For this reason you may want to explicitly set the initial value for a property when defining the GraphObject, since that value will remain as the default value if the Binding is not evaluated.

Bindings are not necessarily evaluated in any particular order. Binding sources should not be (or depend in a conversion function on) the category of the data if you might be modifying the category (e.g. by calling Model#setCategoryForNodeData), because then some bindings might be evaluated before or after the category has been changed.

Conversions

Sometimes the data value needs to be modified or converted in order to be used as the new value of a GraphObject property. The most common conversion functions are provided for you -- they convert a string to a geometric class: Point.parse, Size.parse, Rect.parse, Margin.parse, Spot.parse, and Geometry.parse. But you can easily define your own conversion function.

As an example of a conversion function, let's use a function that adds some text prefixing the data property value:

  new go.Binding("text", "say", v => "I say: " + v)

Although simple conversions cover almost all binding cases, there are some infrequent uses that are covered by "Advanced Conversions", discussed below. Conversion functions must not have any side-effects. Conversion functions may be called frequently, so they should be fast and avoid allocating memory. The order in which conversion functions are called is not specified and may vary.

OneWay and TwoWay Bindings

By default bindings are Binding.OneWay. OneWay bindings are evaluated when the Panel#data property is set or when you call Panel#updateTargetBindings or Model#setDataProperty. OneWay bindings only transfer values from the source to the target.

TwoWay bindings are evaluated in the source-to-target direction just as OneWay bindings are evaluated. However when the GraphObject target property is set, the TwoWay bindings are evaluated in the target-to-source direction. There is no point in having a TwoWay binding on a GraphObject property that cannot be set. For efficiency, avoid TwoWay bindings on GraphObject properties that do not change value in your app.

You should not have a TwoWay binding with a source that is a node data object's key property, i.e. on the data property whose name is the same as the value of Model#nodeKeyProperty. Unintentionally changing the node key value to be the same as another node data's key value may cause indeterminate behavior. Furthermore, changing a node data key without changing any references to that node using the key value will result in "dangling" references and inconsistent relationships. You can make that change safely by calling Model#setKeyForNodeData, but not via a data binding.

The target-to-source update can also go through a conversion function. The most common back-conversion functions are provided for you. They convert a geometric class to a string: Point.stringify, Size.stringify, Rect.stringify, Margin.stringify, Spot.stringify, and Geometry.stringify.

It is common to want to update some data properties based on changes to the diagram. For example, as the user changes the Part#location by dragging a Node, you can automatically keep the node's model data in sync using a TwoWay binding.

  new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify)

The call to Binding#makeTwoWay changes the Binding#mode to be Binding.TwoWay and specifies the Binding#backConverter function to be the Point.stringify static function.

Because the Binding is on the whole node (template), the target object is the whole Node and the target property is "location". The value of data.loc will be a string representation of the Node.location value.

Binding Sources

The target of a Binding is always a property of a GraphObject or a RowColumnDefinition. The source of a Binding is normally a property of a data object in the model. But it is also possible to have the source of a Binding be the shared JavaScript object that is the value of Model#modelData. You can specify such a binding by calling Binding#ofModel, meaning "a binding of a source that is a property of the Model.modelData".

As an example, you might want all Nodes to use the same color. It would be possible but not natural to bind to a property on the node data object, because that property would have to be duplicated on all of the node data objects in the model, and updating the property would mean calling Model#setDataProperty on each node data object with the same new value. Furthermore if there happened to be no nodes at all in the model, there would be no place to save the data. Hence using the shared Model#modelData object would be the sensible place for that shared information.

  new go.Binding("stroke", "strokeColor").ofModel()

and to set or modify that color one would just call, within a transaction:

  model.setDataProperty(model.modelData, "strokeColor", "red");

That would cause all nodes with that model data binding to be re-evaluated. It is not commonplace to have a TwoWay Binding on "ofModel" Bindings, but that should work. Converters also work with "ofModel" Bindings.

And it is also possible to have the source of a Binding be another GraphObject that is in the same Part. You can enable such a binding by calling Binding#ofObject, meaning "a binding of a source that is a property of a GraphObject". You just have to make sure that object has a unique GraphObject#name or is the Part itself. The source property on the GraphObject has to be settable, and the Part must have a value for Panel#data. (If the source property setter does not notify about property value changes, the binding mechanism will not be invoked. Similarly, if there is no Panel.data, the binding mechanism is not active.)

As a common kind of example of data binding between two properties of GraphObjects, consider this Binding on a Shape which changes the color of the Shape#stroke depending on whether the Node is selected (Part#isSelected):

  new go.Binding("stroke", "isSelected", s => s ? "dodgerblue" : "gray").ofObject()

Note the call to Binding#ofObject, which tells the Binding that it should use as the source a GraphObject with a particular name. However that name argument is optional -- supplying no name (or supplying an empty string) will cause the binding to operate with the root GraphObject. In this case that would be the Node itself. Now with this binding whenever the value of Part#isSelected changes, this Shape's stroke changes color. The conversion function is what changes the boolean "isSelected" value to a brush color specifier.

Advanced Conversions

The binding functionality also has more advanced features for less common situations. The source property name may be an empty string, to convert the object as a whole. Conversion functions may take a second argument that takes the object that is bound. For source-to-target conversions, the second argument will be the GraphObject whose property is bound. For target-to-source (back-)conversions, the second argument will be the source data object and the third argument will be the Model.

Here's an example of a two-way data-binding using two custom conversion functions working with two separate data properties. First we define the two conversion functions.

  function toLocation(data, node) {
    return new go.Point(data.x, data.y);
  }

  function fromLocation(loc, data, model) {
    model.setDataProperty(data, "x", loc.x);
    model.setDataProperty(data, "y", loc.y);
  }

Then to data-bind the default template's Part#location property to two separate data properties, "x" and "y":

  new go.Binding("location", "", toLocation).makeTwoWay(fromLocation)

An empty string argument for the sourceprop parameter indicates that the whole data object should be passed to the toLocation function, rather than the value of some property of that data. The return value is used as the new value for the Part#location property. In almost all cases the second argument is not used. Caution: for efficiency reasons you should try to avoid using an empty source property name. Such bindings will be evaluated much more frequently than ones whose source is a particular property name.

The binding works normally for the source-to-target direction. But when the target property is modified it is the source property that is set with the back-converted property value from the target object. Because in this example the source property name is the empty string, and because one cannot replace the whole source data object, any return value from the conversion function is ignored. Instead the conversion function has to modify the data object directly, as this example fromLocation function does.

Note that because the source property name is the empty string, the binding system will not know which properties are modified in the call to fromLocation. Hence to support undo and redo, in order to make the data changes we have to call Model#setDataProperty so that the UndoManager can record the change, including the previous value.

Replacing Items in Arrays

However, although a TwoWay Binding cannot replace the node data object in the Model#nodeDataArray, it is possible to replace an item in an Panel#itemArray. So if your node data were:
  { key: 1, items: ["one", "two", "three"] }

And if your node template included something like:

    $(go.Panel, "Vertical",
      new go.Binding("itemArray", "items"),
      {
        itemTemplate:
          $(go.Panel,
            $(go.TextBlock, { editable: true },
              new go.Binding("text", "").makeTwoWay())
          )
      }
    )

Then the user would be able to edit any of the TextBlocks, causing the item Array to be modified, for example resulting in this node data:

  { key: 1, items: ["one", "SOME NEW TEXT HERE", "three"] }
Available Extensions
Annotations
  • @JS()
  • @staticInterop

Constructors

Binding([String? targetprop, String? sourceprop, TargetConversion? conv, BackConversion? backconv])
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 Properties

oneWay EnumValue
This value for Binding#mode uses data source values and sets GraphObject properties. Bindings are evaluated when Panel#updateTargetBindings is called.
getter/setter pair
twoWay EnumValue
This value for Binding#mode uses data source values and GraphObject properties and keeps them in sync. When Panel#updateTargetBindings is called, the GraphObject properties are set. When GraphObject properties are modified, the Panel#data properties are set.
getter/setter pair

Static Methods

parseEnum(EnumValue defval, [dynamic ctor]) EnumValue Function(String)
This static function can be used to create a function that parses a string into an enumerated value, given the class that the enumeration values are defined on and a default value if the string cannot be parsed successfully.
toString$([dynamic val]) String
This static function can be used to convert an object to a string, looking for commonly defined data properties, such as "text", "name", "key", or "id". If none are found, this just calls toString() on it.