Binding to a property of a property

Go.js Ver 1.6.11

Is it possible to bind to a property of a property on node data, like so? All of the examples I’ve seen are only single level deep bindings, but I can’t find anything that’s restricting it one way or the other. I’ve tried several possibilities and none of them have worked. Hoping I’m just missing something.

GO(go.Picture,
  {
    desiredSize: new go.Size(TASK_NODE_STATUS_ICON_SIZE, TASK_NODE_STATUS_ICON_SIZE)
  },
  new go.Binding("element", "metadataObject/status", getTaskStatusImage)
)

Yes, it is possible. Good try, but you need to implement smarter Bindings. Look at how this sample implements bindings on source properties that are subproperties of a data property named “details”:

Also:

Am I reading these right, you still have to manually trigger binding updates with updateTargetBindings if you’re using a sub property, and can’t rely on the binding detecting changes automagically?

It looks like that sample is doing the same thing I was doing, just not quite the same way or as cleanly as your example.

GO(go.Picture,
  {
    desiredSize: new go.Size(TASK_NODE_STATUS_ICON_SIZE, TASK_NODE_STATUS_ICON_SIZE)
  },
  new go.Binding("element", "", getTaskStatusImage)
)

function getTaskStatusImage(nodeData) {
  var status = getStatusFromMetadata(nodeData);
  var imageSrc = "";

function getStatusFromMetadata(nodeData) {
  return nodeData.metadata.getStatus();
}

In this setup, I still have to manually trigger updates, as the bindings obviously can’t tell I’ve changed that internal property. I was just hoping there was a way to get around that, as I can have metadata updates come from many different places, and I’d rather not have to do anything weird like pass references to the diagram around in my metadata.

The two options I had considered is putting a reference to the go.js node on my metadata manager and then using it’s getter/setter functions to trigger updates or just copy/replacing the entire metadata when an update happens, which should in theory retrigger the binding, though that might be a bit heavy handed.

Is there any way to do a subproperty binding which allows the binding to be automatically notified of changes?

Are you saying that you are not calling Model.set (a.k.a Model.setDataProperty)?

If you are not calling that method, then the property being on a sub-object doesn’t matter. The reason you need to call Model.set is two-fold: to notify about the change (to allow all Diagrams to update) and to record the previous value so that undo/redo can work.

Whether you are calling Model.set on some Node.data or on aNode.data.metadata doesn’t really matter.

These are properties of individual objects, not of the diagram.model as a whole.

I’m attaching the metadata in a very naive fashion, just setting the metadata object as a property on the node itself.

    _.each(this.expandedNodes, function (node) {
        node.metadata = self.getNodeByID(node.id);
    });

Is there a more nuanced way to attach a model object to the nodes? There’s only a few values of the metadata that are useful to the nodes, so it isn’t a full on ‘model’ for them.

We also don’t support any undo/redo, as the diagram itself is basically static, with all the manipulation that would affect the diagram being done elsewhere in the app.

What is the type of node in your code? What does self.getNodeByID return?

As you know, there’s no Node.metadata property, so of course GoJS cannot depend on it.

The node is the data object that will eventually be passed in to nodeDataArray:

bounds:{x: 550, y: 2269, width: 29, height: 38}
dataType:""
displayName:"Correlation Matrices"
id:"_2"
name:"FW_CORR.CONFIG_CORR_MATRIX"
__proto__:Object

getNodeById returns the POJO metadata object.

I don’t expect go.js to depend on it or anything, I’d just like a way to bind to it’s subproperties, and have them update when the reference changes. If that requires some changes to my metadata object to conform to a go.js model or anything, that’s fine.

Yes, if you want bindings to work, you should call Model.set whenever you want to change any data properties.

But the binding works fine when I just bind to that metadata object itself. It’s only the sub-properties I can’t bind to.

Is there an equivalent model.set for individual nodes, or only the diagram as a whole?

Oh, nevermind to the second question. I was apparently looking at the wrong thing. I see you can pass the nodeData as part of the setDataPropert() call. So I’d just do model.setDataProperty(nodeData, “status”, nodeData.metadata.status), or something?

Basically, I’d like to get rid of the many, many places I have to do code like this:

    for (var key in updates) {
        var update = updates[key];
        if (update.status === STATUS_RUNNING) {
            runningIds.push(update.id);
        }
        var nodeData = model.findNodeDataForKey(update.id);
        if (nodeData) {
            model.setDataProperty(nodeData, "status", update.getStatus());
        }
    }

And instead just have the code that already goes and iterates over my ajax return to update the metadata objects automatically update the diagram via bindings.

I’m not sure what you are asking for, but it sounds like you could use Model.applyIncrementalJson.