findNodeForData and findNodeForKey

Hello folks,

I’ve been having problems retrieving a node from the diagram. I do not know exactly what the problem is, and the documentation on the Node class is quite unclear on how to implement the findNodeFor* methods.

A bit of background: our application (Extjs 4 based for now) has GoJS included in a wrapper object. We attach a diagram to an Ext.container.Container (in essence a DIV component with some fancy bits tacked on) and add nodes to it using the nodeDataArray method on go.GraphLinksModel.

However, our application has the possibility of showing many workflows at once, so there is not just 1 diagram; there can be many at once.
This means that a lot of logic has gone into making sure that the correct diagrams are being controlled at any one time.

Still with me? Good!

See here a snippet of code
<br /> this.canvas[this.taskId].startTransaction("moveGroupNodes");<br /><br /> var inputNode = this.canvas[this.taskId].findNodeForData({"name": "aggregator_in"});<br />

Here I try to get from a specific diagram (called canvas here) (as identified by this.taskId) a node with name “aggregator_in”.
Every canvas has exactly one of these. However, I cannot ever find this node. findNodeForData does not return this node. Neither does findNodeForKey with it’s proper parameter. I’ve checked in the console whether I can locate this node myself and it is indeed available in
<br />this.canvas[this.taskId].nodes<br />

Same scope, same controller, same view.

The documentation is unclear on what constitutes a correct parameter for these methods:
I would expect that findNodeForKey would take a parameter of type String which should exactly match the key of the Node you want. This does not seem to work though.

findNodeForData is even more unclear: so far I’ve been able to figure out that it may need an object of parameters that you want to match the Node to, but if I make the above findNodeForData call the return value is null.

I do not know how to proceed from here.
Any help is appreciated.

Diagram.findNodeForKey takes “key” values, which must be numbers or strings that uniquely identify each node data in the Diagram.model and are stored as a property on that node data. By default it uses “key” as the name of that property, but you can use a different property name if you like by setting Model.nodeKeyProperty before setting Model.nodeDataArray.

You didn’t show an example of your node data, so I don’t know if:

var diagram = this.canvas[this.taskId]; diagram.findNodeForKey("aggregator_in")
would work or not.

Diagram.findNodeForData finds the Node corresponding to a node data object that is in the Diagram.model. It does not do a search, so the argument is not a bunch of named parameters for a search – the argument is the node model data itself, which is also the value of Node.data for the Node that you are looking for.

Diagram.findNodeForKey is basically implemented as findNodeForData(model.findNodeDataForKey(k)).

Models only index the data in the Model.nodeDataArray by the Model.nodeKeyProperty, not by any other properties. So you might need to do the search yourself.

Allright, I guess a little more is necessary at this point then. Below is the method responsible for retrieving and adding the node model data to the diagram:

This code is part of an ExtJS 4 controller class, and as such this is all in a class method. The first 10 lines or so are ExtJS request handling so of little interest to the GoJS side of things.
<br />// All Ext<br />doRequest: function() {<br /> "use strict";<br /> var that = this;<br /> Ext.Ajax.request({<br /> url: "/get-workflow-data",<br /> scope: this,<br /> success: function (response, opts) {<br /> var resp = Ext.decode(response.responseText),<br /> group = [];<br /> // GoJS from here<br /> // Create a list of nodes, starting with the fixed input and output connectors <br /> that.elements.nodes.push({<br /> key: that.taskId + "_in",<br /> name: "aggregator_in",<br /> fill: "green",<br /> type: "in",<br /> category: "connector",<br /> selectable: false,<br /> movable: true<br /> });<br /> that.elements.nodes.push({<br /> key: that.taskId + "_out",<br /> name: "aggregator_out",<br /> fill: "red",<br /> type: "out",<br /> category: "connector",<br /> selectable: false,<br /> movable: true<br /> });<br /> // Then we add the nodes that the backend returned in the JSON response<br /> Ext.Array.each(resp.tasks, function(node, index, allItems) {<br /> var groupKey = node.groupKey || "No Group";<br /> that.elements.nodes.push({<br /> key: node.id,<br /> name: node.title,<br /> editable: true,<br /> group: groupKey,<br /> loc : node.x.toString()+ " " + node.y.toString()<br /> });<br /> // Ext.Array.include creates an array where each key is unique<br /> Ext.Array.include(group, groupKey);<br /> });<br /> that.groupList[this.taskId] = group;<br /> if (that.groupList[this.taskId].length > 1) {<br /> Ext.Array.each(that.groupList[this.taskId], function(group, index, allItems) {<br /> // if there is only a "No Group" value for groups, we don't create any groups at all<br /> if (group !== "No Group") {<br /> that.elements.nodes.push({<br /> key: group,<br /> isGroup: true<br /> })<br /> }<br /> });<br /> }<br /> Ext.Array.each(resp.connections, function(edge, index, allItems) {<br /> // Set up links, making sure that any that go the the containing tasks are visually linked to our<br /> // fake connectors<br /> if (edge[0] === that.taskId) {<br /> edge[0] = that.taskId + "_in";<br /> }<br /> if (edge[1] === that.taskId) {<br /> edge[1] = that.taskId + "_out";<br /> }<br /> that.elements.edges.push({<br /> from: edge[0],<br /> to: edge[1]<br /> });<br /> });<br /> // Set the node model and put everything in its place<br /> // A few toolmanagers and the like are in here but removed for here<br /> that.nodeModel = that.gojs(go.GraphLinksModel);<br /> that.nodeModel.nodeDataArray = that.elements.nodes;<br /> that.nodeModel.linkDataArray = that.elements.edges;<br /> that.canvas[that.taskId].model = that.nodeModel;<br /> that.canvas[that.taskId].nodeTemplateMap = that.templateMap;<br /> that.canvas[that.taskId].linkTemplate = Workflow.Templates.linkTemplate();<br /> that.canvas[that.taskId].groupTemplate = Workflow.Templates.groupTemplate();<br /> }<br /> });<br />},<br />

As for findNodeForData, if I understand correctly, findNodeForData only works if you already have the node data?
Why would I want to find the node by data if I already have the data in the first place?

What I’d like to know is, how do I find a node from the diagram if I only have a part of the data, such as just the key, or just a name or type (or perhaps another property on the node model).

The name of the method confuses me, from the name I would assume that it finds a node in the model somewhere (a search, if you will), when it doesn’t in fact do any such thing.

Thank you for your assistance so far.

The Diagram.findNodeForData and findNodeForKey methods find a Node in the Diagram corresponding to some model data or some key for the model data. The Model.findNodeDataForKey method finds a node data object in the model.

The mapping in the other direction is easier: Node.data or Node.data.key.

If you have the key, you can call either Model.findNodeDataForKey or Diagram.findNodeForKey. For any other property values, you’ll have to search for yourself.