Links don't save their points

I am using goJS to show associations between specifically selected points of data in the style of the Concept Map sample, however when I reshape a link between two nodes it doesn’t always update the diagram model, despite .makeTwoWay();

Each node on the diagram will have at least other connected node, and each connected node will have 2 links (to and from)… One of these links is capable of being moved and stored, and one isn’t with the exception of a node that has 6 links (3 to and 3 from) with another node.

I discovered the problem when extracting the diagram model and storing it, as not all the links would be where I placed them. I then tried doing diagram.rebuildParts() on the ‘LinkReshaped’ event, and this just reset them to where they were before if they weren’t saved.

My node template is as follows:

oDiagram.nodeTemplate = goJs(go.Node, "Auto", { 
  avoidableMargin: new go.Margin(50,50,50,50),
  resizable: true,
  layoutConditions: go.Part.LayoutStandard & ~go.Part.LayoutNodeSized },
  // define the node's outer shape, which will surround the TextBlock
  goJs(go.Shape, { 
    name: "SHAPE",
    stroke: "black",
    strokeWidth: 2
  },
  new go.Binding('fill', 'colour'),
  new go.Binding('geometryString', 'geoString')),  goJs(go.TextBlock, { 
    font: "bold 10pt helvetica, bold arial, sans-serif",
    margin: 4 
  },
  new go.Binding("text", "text")),
  new go.Binding("location", "loc", go.Point.parse),
  new go.Binding('width', 'width'),
  new go.Binding('height', 'height')
);

My linkTemplate is as follows

[code]

oDiagram.linkTemplate = goJs(go.Link, {
routing: go.Link.Normal,
reshapable: true,
toolTip: goJs(go.Adornment, “Auto”,
goJs(go.Shape, { fill: “lightyellow” }),
goJs(go.Panel, “Vertical”,
goJs(go.TextBlock, {
font: “bold 12pt helvetica, bold arial, sans-serif”,
textAlign: “center”,
margin: 3
},
new go.Binding(“text”, “text”)
)
)
)}, goJs(go.Shape, {
isPanelMain: true,
stroke: “black”,
strokeWidth: 2
}),
goJs(go.Shape, {
toArrow: “standard”,
stroke: null
}),
goJs(go.Panel, “Viewbox”, {
segmentIndex: 0,
segmentFraction: 0.4
},
goJs(go.TextBlock,
new go.Binding(“text”, “text”)
)
),
new go.Binding(“points”).makeTwoWay()
);[/code]

A JSON excerpt of my nodeDataArray


[
  {
    "key": 8,
    "text": "Allow Users and Admins to be Combined",
    "loc": "-899.03 771.42",
    "colour": "#FEC900",
    "__gohashid": 25534
  },
  {
    "key": 22,
    "text": "Self assignment of licenses",
    "loc": "-580.05 992.81",
    "colour": "#FEC900",
    "__gohashid": 25535
  },
  {
    "key": 23,
    "text": "URL for creating sample data is broken",
    "loc": "-772.04 1225.53",
    "colour": "#FEC900",
    "__gohashid": 25536
  },
  {
    "key": 24,
    "text": "Allow us to send emails to client accounts",
    "loc": "-198.14 927.76",
    "colour": "#FEC900",
    "__gohashid": 25537
  }
]

A JSON excerpt of my linkDataArray


[
  {
    "assoc": "694",
    "from": 8,
    "text": "associated with",
    "to": 22,
    "points": [
      -750.86,
      795.72,
      -630.92,
      898.17,
      -504.72,
      992.81
    ]
  },
  {
    "assoc": "694",
    "from": 23,
    "text": "associated with",
    "to": 22,
    "points": [
      -637.87,
      1225.53,
      -563.65,
      1124.11,
      -497.72,
      1017.11
    ]
  },
  {
    "assoc": "694",
    "from": 24,
    "text": "associated with",
    "to": 22,
    "points": [
      -125.71,
      952.06,
      -266.56,
      967.49,
      -405.98,
      992.81
    ]
  },
  {
    "assoc": "693",
    "from": 22,
    "text": "associated with",
    "to": 8,
    "points": [
      -504.73,
      992.81,
      -624.67,
      890.36,
      -750.86,
      795.72
    ]
  },
  {
    "assoc": "693",
    "from": 22,
    "text": "associated with",
    "to": 23,
    "points": [
      -497.72,
      1017.11,
      -571.94,
      1118.53,
      -637.87,
      1225.53
    ]
  },
  {
    "assoc": "693",
    "from": 22,
    "text": "associated with",
    "to": 24,
    "points": [
      -456.18,
      992.81,
      -252.46,
      918.63,
      -162.19,
      927.76
    ]
  },
]

Does anyone have an idea of what is causing the problem, or how to fix it?

Calling Diagram.rebuildParts upon the user’s reshaping of a link seems very wasteful and not likely to help.

I can’t tell from the data that you listed – are all of the Points recorded in the model for the routes of the Links correct? In other words, is this only a problem upon loading, not upon saving?

At the moment I don’t see anything obviously wrong with your templates, although in an unrelated issue I think the Viewbox Panel is unnecessary in the link template. How have you defined your Diagram?

The calling of Diagram.rebuiltParts() when reshaping a link was mostly so I could confirm their points weren’t being saved, before that I’d have to save my diagram and then re-import it to find out which ones have been saved or not.

All of the links and points are valid data and contain all the correct bindings, and this problem is caused with saving. The data is all loaded and the links are placed in the correct positions according to their points, it’s just that not all of the links update their points when reshaped.

The diagram is defined as such:


oDiagram = goJs(go.Diagram, oData.divId, { 
  layout: fLayout(),
  allowVerticalScroll: true,
  allowHorizontalScroll: true,
  allowZoom: true,
  allowMove: true,
  allowDelete: false,
  allowCopy: false,
  padding: 100,
  "toolManager.hoverDelay": 500
});

where ‘fLayout’ is a function that decides whether to return

goJs(go.Layout, {});

or

goJs(go.ForceDirectedLayout, {
  defaultElectricalCharge: 1000,
  defaultSpringStiffness: 0.1,
  defaultSpringLength: 50
});

The first layout is used if this diagram has been built before and is being passed a goJs model, the second occurs if the diagram has only just been requested and exists so that all of the data is laid out in a human-viewable manner after being sculpted into a form readable by goJs… It is with the first layout (an existing diagram) that the issue is occuring with

First, OK, you’re using Diagram.rebuildParts() just for debugging purposes.

Second, I still don’t understand where/when the information is wrong. Here’s a list of things that one wants to be true after reshaping a link:

(1) it has the desired route (list of points) in memory (i.e. ReshapingTool sets Link.points as instructed)
(2) it appears correctly on the screen (i.e. Link generated the appropriate Geometry from the Link.points list)
(3) Model.toJson produces text with the proper list of points (i.e. “points” is an Array of pairs of numbers on the link data, because of the TwoWay Binding you declared on Link.points)
(4) Loading that model with Model.fromJson results in the same route in memory and link geometry
Which of these states are not the case in your app?

Third, it’s clever for you to generate the Diagram with two different layouts, but it isn’t necessary. You can unconditionally set Diagram.layout to what you (sometimes) want to perform, but not have it perform after loading the model or after certain structural changes to the diagram, by setting Layout.isInitial to false and setting Layout.isOngoing to false. The Page Flow sample and perhaps other samples do this. That way you can completely control when a layout actually happens by calling Diagram.layoutDiagram, with a boolean argument depending on what you want.

All 4 are incorrect, I have taken some screenshots to help illustrate my case, with Diagram.rebuiltParts() being commented out to help illustrate my point.

I load in my desired diagram from the database, it is displayed as follows (all correct so far).

I then decide to change the paths of some of my links, maybe the node has too many associations and it’s a little hard to read.

I am satisfied with my new link paths, I save the diagram and then have it reload.

One of my links has not had its path saved, instead it keeps the path originally specified from when I first created the diagram. The same holds true for my links between the other nodes on this diagram, and on all the other diagrams created/viewed within this particular app.

As for the third point, I didn’t realise that was possible. I will revisit the samples and edit my code as appropriate. Thank you for informing me, that sounds like a much easier option.

That is very odd. I wonder how your app is different from the State Chart sample: http://gojs.net/latest/samples/statechart.html

Just to be certain, I modified the State Chart sample to remove the binding of Link.curviness and the setting of Link.curve so that the links are straight rather than Bezier curves. It still worked as I think you would expect.

I have managed to recreate the issue on a JSFiddle, if you want to take a closer look.

http://jsfiddle.net/kk9mw2ve/

I’ve also removed the unnecessary ‘Viewbox’ on the off-chance it was that, but that hasn’t changed anything.

First, I noticed that you didn’t have a a TwoWay Binding on the Node.location. That meant that when a node was moved, its location was not saved.

Second, I reproduced what you are reporting. The difference is that the State Chart sample has Link.adjusting set to go.Link.Stretch. I’m not sure why that should matter, but perhaps it will work for you if you also set that property in your link template. We’ll investigate what’s going on to see if there’s some sort of bug there.

I manually applied location storage onto my diagram model when saving, I completely forgot I could use .makeTwoWay(). Oops!

Also, I’ve added ‘adjusting: go.Link.Stretch’ to my link template and it seems to fix the issue completely.

Thank you for your help

I’ve come across an issue with the above fix of using

adjusting: go.Link.Stretch

to my LinkTemplate.

Sometimes my links will render like this:

The highlighted link is between the ‘Dynamic Menu System’ (blue cog) and ‘Comparison not shown on Admin menu’ (far left arrows with dotted lines) nodes, yet it stretches off the screen and requires a fair amount of scrolling to reach the middle segment of it and resize it appropriately. This is the same for the other link between these two nodes, however that one goes to the opposite end of the canvas…

I experimented what would happen with changing ‘go.Link.Stretch’ to ‘go.Link.Scale’, in case that would fix the issue.
This caused the same link to not render properly either:

The middle segment of the link is shown with its’ text, but nothing else is visible. Upon clicking the segment to reshape the link it reshapes itself correctly:

I also tried using ‘go.Link.End’ but that style of link is not suitable for the diagram, as well as returning to the default of ‘go.Link.None’… However that caused the issue this thread was originally for to resurface. Using the same data structure as before, the ‘key’ attributes of both the nodes line up correctly with the ‘to’ and ‘from’ attributes used by each of the links.

Worryingly, it only ever happens between these two nodes. Is it maybe something to do with the ‘go.ForceDirectedLayout’… This happens when I used the default options for the layout or pass in my own, of any values I’ve tested.

1 Like

I am speculating that the wild routes that you sometimes get are due to the ForceDirectedLayout moving the nodes around after the routes have been specified. I didn’t know you were using ForceDirectedLayout – why do you need it when you are saving and restoring the node locations?

If you do want an automatic force-directed layout after some nodes or links have been added or removed, that’s OK. But you might want to set Layout.isInitial = false on your Diagram.layout so that when the model is loaded it doesn’t cause a layout.

I believe the need for setting Link.adjusting to go.Link.Stretch (or go.Link.Scale) will go away in version 1.4.12, which fixes a bug that improperly invalidates the routes of some links when loading a model.

That release should be happening in a few business days, after we have had some more testing and any other bug fixes in the pipeline have cleared.