Update adjacent nodes when links are added/modified/deleted


#1

Hello,

we want to display nodes differently (e.g. different background color) depending on their incoming and outgoing links.
So for example if a node has a link with category=categoryA it should have a blue background and in case of category=categoryB a yellow background.

First I thought I could bind the node’s background to a property and update this property whenever the linkDataArray is modified.
However, the documentation says I shouldn’t update the model from within the change listener (while a transaction is still active) (please correct me if I am wrong, cannot find the part of the doc where I’ve read this currently)

The only other option I can think of is to modify the adjacent nodes of the modified/removed/added link directly from the change listener.

Is there a better way?


#2

You should be able to use Node.linkConnected and Node.linkDisconnected to update the node as the link connections are changed. For example, this will change the node’s color if there is any “categoryA” link connected to it:

function updateNodeColor(node, link, port) {
  var model = myDiagram.model;
  model.commit(function(m) {
    var hasCatALink = node.findLinksConnected().any(function(l) {
      return l.category === "categoryA";
    });
    m.setDataProperty(node.data, "color", hasCatALink ? "red" : node.data.color);
  });
}

// define a simple Node template
myDiagram.nodeTemplate =
  $(go.Node, "Auto",  // the Shape will go around the TextBlock
    {
      linkConnected: updateNodeColor,
      linkDisconnected: updateNodeColor
    },
    $(go.Shape, "RoundedRectangle", { strokeWidth: 0 },
      // Shape.fill is bound to Node.data.color
      new go.Binding("fill", "color")),
    $(go.TextBlock,
      { margin: 8 },  // some room around the text
      // TextBlock.text is bound to Node.data.key
      new go.Binding("text", "key"))
  );

#3

By the way, you do not have to have a property on the model node data to control the color (or any other properties) of the Node or its GraphObjects. For example, you could directly change the Shape.fill of some shape in the node. If there were no Binding targeting that property, it would still be OK, because when loading an existing model, the repeated calls to updateNodeColor would eventually get the node’s color correct.


#4

Thanks for the replies! I didn’t know about linkConnected and linkDisconnected.
These method seem to handle new and deleted links, but in our application there is also the possibility to modify the category of an existing link, and depending on that change I need to update the adjacent nodes as well.

Therefore I have a ModelChangedListener in which I am setting a property on the node:

if (e.change === go.ChangedEvent.Property && e.modelChange === "linkCategory") {
    const link = diagram.findLinkForData(e.object);
    diagram.model.setDataProperty(link.fromNode.data, "_valid", isValid);
}

An undo of the link category modification transaction would then also undo the node._valid property, which I use for a color binding.

Does this look reasonable or should I reconsider this approach?


#5

I think that’s OK.

We did not know you wanted to change properties on a link and have that automatically affect connected nodes. That cannot be achieved with data binding, so I believe using a Changed listener will work. You do need to be careful to implement it to get only the results that you want in all combinations of events that might happen.


#6

Great, thanks!