How to detect if proposed link violates validCycle

I was trying to figure out why dragging from one port to another was failing – it was because I set validCycle to a non-default value.

I want to highlight all valid destinations as soon as the user starts to drag a port.

I would also like to log (or perhaps show on screen or with tooltip) WHY the user can’t drop over a particular port.

Is there any way I can tell if/when/why a validCycle setting is preventing a target from being used?

I made a copy of samples/minimal.html and modified its JavaScript, as follows:

[code] function init() {
if (window.goSamples) goSamples(); // init for these samples – you don’t need to call this
var $ = go.GraphObject.make; // for conciseness in defining templates
myDiagram = $(go.Diagram, “myDiagram”, // create a Diagram for the DIV HTML element
{
initialContentAlignment: go.Spot.Center, // center the content
“undoManager.isEnabled”: true // enable undo & redo
});

var tool = myDiagram.toolManager.linkingTool;
tool.doActivate = function() {
go.LinkingTool.prototype.doActivate.call(tool);
// turn all port strokes to be either green or red depending on whether isValidLink is true
myDiagram.skipsUndoManager = true;
var startport = tool.startPort;
var startnode = startport.part;
myDiagram.nodes.each(function(node) {
node.ports.each(function(port) {
if (tool.isForwards) {
port.stroke = tool.isValidLink(startnode, startport, node, port) ? “green” : “red”;
} else {
port.stroke = tool.isValidLink(node, port, startnode, startport) ? “green” : “red”;
}
});
});
myDiagram.skipsUndoManager = false;
};
tool.doDeactivate = function() {
go.LinkingTool.prototype.doDeactivate.call(tool);
// restore all ports to normal color, here assumed to be black
myDiagram.skipsUndoManager = true;
myDiagram.nodes.each(function(node) {
node.ports.each(function(port) {
port.stroke = “black”;
});
});
myDiagram.skipsUndoManager = false;
};

// define a simple Node template
myDiagram.nodeTemplate =
  $(go.Node, "Auto",  // the Shape will go around the TextBlock
    $(go.Shape, "RoundedRectangle",
     <font color="#ff0000"> { portId: "", fromLinkable: true, toLinkable: true, cursor: "pointer" },
      { strokeWidth: 3 },</font>
      // Shape.fill is bound to Node.data.color
      new go.Binding("fill", "color")),
    $(go.TextBlock,
      { margin: 3 },  // some room around the text
      // TextBlock.text is bound to Node.data.key
      new go.Binding("text", "key"))
  );

// but use the default Link template, by not setting Diagram.linkTemplate
// create the model data that will be represented by Nodes and Links
myDiagram.model = new go.GraphLinksModel(
[
  { key: "Alpha", color: "lightblue" },
  { key: "Beta", color: "orange" },
  { key: "Gamma", color: "lightgreen" },
  { key: "Delta", color: "pink" }
],
[
]);

}[/code]
First I turned the node’s Shape into a port and made them interactively linkable. I also removed all of the predefined link data in the model.

Second I decided to use the port’s stroke to show whether it’s OK to link with that port. To make it easier to see, I set the strokeWidth to 3. Valid ports’s strokes would turn green, invalid would turn red. Of course you may have other ways of showing this information.

Third I modified the LinkingTool, myDiagram.toolManager.linkingTool. This is JavaScript, so I just clobbered that particular tool in order to override the doActivate and doDeactivate methods, but you could have defined a subclass of LinkingTool. (There are several examples of this in the samples; I recommend you also read: http://gojs.net/latest/intro/extensions.html, if you haven’t already.)

You can see the overrides in the code above. Each iterates over all of the ports of all of the nodes in the diagram. Although usually new links are drawn “from” to “to”, in some situations they are drawn “backwards”, which is why there are two calls to isValidLink.

Regarding your additional question about showing WHY a link would be invalid, it would be easy for you to show a tooltip or some temporary Adornment in an override of doMouseMove when the mouse is over an invalid port (or when it isn’t over or near any valid port, if you like), but it isn’t clear to me what you would say. I suspect that information is too application-specific for me to speculate about.

Thanks for the detailed reply Walter.

This is close to what I’ve already done – actually I’ve overridden almost every link related message so that I can trace them out to see the flow of things.

I do see a few things in your example that I can use to make my code a bit cleaner.

It’s just that “isValidLink” might come back with “false” but not say why – might be nice if it optionally gave me a reason. Is there some internal method I can call, like “checkCycleValid”?
I had a port that I could not drag to and it took me quite a while to figure out it was the cycle setting. I could see an end user trying to add to a diagram, and not being able to. I might have status bar text at the bottom of the screen that says “linking to this port would create a cycle in the graph” or something like that.

If there’s no built-in way to do this that’s ok, I can think of work arounds to apply later.
I could pull the graph into a datastructure and enforce cycles myself, or run the valid checks twice if they come back false, once without the flag on, once with it off to see if that was the cause.

The problem is that there are probably dozens of reasons why a proposed link might not be valid, depending on the values of many properties as well as the particular configuration of the existing graph. Furthermore you may have set LinkingBaseTool.linkValidation or Node.linkValidation, so there can be arbitrarily complex reasons for a failure.
Also, there might be multiple reasons that apply at a particular time, and some of those reasons might not apply to your particular application. And of course as soon as the linking tool detects that a possible link is invalid, it stops immediately, so it doesn’t even get a chance to find out additional reasons why it should fail.

If you read the documentation for LinkingBaseTool.isValidLink and the documentation for all of the things it mentions, etc., you’ll get a fairly complete list of the possibilities.

I suppose we could make public a method like what you mention.

I would think you could come up with the reason(s) why a link would be invalid just by examining the ports, and otherwise assume it would create some kind of cycle.