Change in 2.0.0 when assigning keys to nodes dropped from palette

Hi
I have a palette and a canvas setup. I drag from the palette to the canvas and its all good. If I drag a node from the palette to the canvas and there is already a node with the same key, it sets the newly dragged node key to a negative number. I can deal with that and remove the dragged node with the negative key, still all good. Separately, I have implemented an undo function where I set the canvas model back to a prior state (see below), also all good. The problem comes when I do both these things. When I have a specific node on the canvas and I undo to a prior state the node is gone, I then drag this same node from the palette to the canvas and I get a negative key even though the node is gone. Very strange. Can you advise if I am doing something wrong.
My undo code:
\
if (_undoStack.length > 0) {
_baseDiag.clear();
_baseDiag.model = go.Model.fromJson(_undoStack.pop());
}
\\

Are you using the standard UndoManager and its functionality, and trying to integrate with that? Or are you implementing your own whole mechanism for undo/redo?

To investigate your situation before the drag-and-drop, you might want to check what key the node has in the Palette.model, and also see if that key value is present in the main Diagram.model.

Is this new property of use to you? Model | GoJS API

Thanks for the quick reply. I am not using the undo manager as it does not fit with our needs. But saving the whole model as JSON and putting is back works just great. In regards to you question: If I have never had that specific node on the canvas and I drag it on, it all works as expected. If however I have had the specific node on the canvas and removed it by loading a previous model as shown in previous post, then I get the negative key (just like it does when there is a duplicate). It appears to somehow remember even though the model has been replaced and the specific node is no longer in the model.

Also, even if I force the new dropped node to have the correct key, the links don’s work, so they are not seeing the correct node key. Therefore this code does not fix it.
\
if (droppedNode.data.key < 0) {
existingNode = _baseDiag.findNodeForKey(droppedNode.data.orgKey);
if (existingNode) {
_baseDiag.remove(droppedNode);
Animate(existingNode);
return;
}
else {
droppedNode.data.key = droppedNode.data.orgKey;
existingNode = droppedNode;
}
}
else
existingNode = droppedNode;
\\

One more thing to note. If I delete the specific node from the canvas and re-drag it on from the palette it works great. It just fails when the model has been re-loaded and the previous model had that specific node

Assuming you do not have any unmodeled Parts (which do not get keys because they are not in the model), you do not need to call Diagram.clear() before replacing the Diagram.model. You were probably just trying different things to get it to work.

I can’t explain why you are having this odd behavior. But I’ll ask again what the key is of the Node in the Palette before the drag-and-drop, and whether it’s in the target Diagram’s model.

Hi

Yes I did add the clear() to try and fix the issue. On the palette the key is 25, when dragged onto the canvas without any prior undo, its 25. On the palette the key is 25, when dragged onto the canvas after an undo, its -4. Any node with a correct key on the palette when dragged onto the canvas after an undo gets a new key of -4 (if that node was on the canvas before the undo). The undo is not using the undo-manager but replacing the whole model. It appears that there is some kind of shadow of the node before the undo. Everything works great before I do a model replace as a form of undo.

Hi I did a bit more testing and can provide more info. If i have a node of key 28 on the canvas and haver never had a node of key 25 on the canvas and perform an undo, then any node i drag onto the canvas (even number 25 that has never been then before) gets a key of -4. The issue appears to be the undo… Once this has happened - any node copied from the palette get a new negative key. All keys on the palette are correct before drag starts. The undo by replacing the model is causing the issue.

Thanks for the additional data, but I still cannot explain it.

Try setting Model.copiesKey to false on the Palette.model.

Hi

Thanks. I tried that, no difference. When I undo by replacing the model with a previous one, everything still works ok, i can continue to add links, nodes etc as expected. The only thing that stops working is the drag from palette to canvas. The drag from palette works great if I don’t do an undo to a previous model. All dropped nodes have a negative key. This is the same result as you get if you drop a node from the palette onto the canvas and the canvas has a node with this key already. I would expect something like that. However in this case, when I findNodeForKey on the canvas using the correct key, it does not exists so why set the dropped key to a negative number. As I said, this only happens when I have performed an undo by replacing the canvas model with a previous version.

Have you looked at the saved model that holds the previous state? Does it have anything using the key 25?

Yes I did and no. The save model only has 3 nodes at that point in time and no links

\
{
“class”:“GraphLinksModel”,
“linkKeyProperty”:“key”,
“nodeDataArray”:[
{
“flagForDelete”:false,
“lastErr”:null,
“key”:21,
“name”:“node 1”,
“description”:“node 1”,
“local”:true,
“alert”:false,
“importedKey”:" “,
“outboundLink1”:” “,
“outboundLink2”:” “,
“nodeTypeKey”:-3,
“color”:”#edb9bd",
“shape”:0,
“icon”:“f2d2”,
“localAllowed”:true,
“nodeTypeName”:“Software Instance”,
“overlayKey”:0,
“overlayName”:"",
“overlayDescription”:"",
“overlayIcon”:"",
“overlayColor”:"",
“childName”:“fred”,
“stamp”:“2019-04-13T10:29:00”,
“author”:“Nelly”,
“propertiesWithFieldDefinition”:[

     ],
     "properties":[

     ],
     "zOrder":0,
     "location":"-0.4000000000000057 -0.28000000000000113"
  },
  {
     "flagForDelete":false,
     "lastErr":null,
     "key":22,
     "name":"node 2",
     "description":"node 2",
     "local":true,
     "alert":false,
     "importedKey":"                                                                                                    ",
     "outboundLink1":"                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    ",
     "outboundLink2":"                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    ",
     "nodeTypeKey":-3,
     "color":"#edb9bd",
     "shape":0,
     "icon":"f2d2",
     "localAllowed":true,
     "nodeTypeName":"Software Instance",
     "overlayKey":0,
     "overlayName":"",
     "overlayDescription":"",
     "overlayIcon":"",
     "overlayColor":"",
     "childName":"fred",
     "stamp":"2019-04-13T10:36:00",
     "author":"Nelly",
     "propertiesWithFieldDefinition":[

     ],
     "properties":[

     ],
     "zOrder":0,
     "location":"-0.14142135623731633 175.3789949493661"
  },
  {
     "flagForDelete":false,
     "lastErr":null,
     "key":26,
     "name":"new thing",
     "description":"fghfghg",
     "local":true,
     "alert":false,
     "importedKey":"                                                                                                    ",
     "outboundLink1":"                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    ",
     "outboundLink2":"                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    ",
     "nodeTypeKey":-3,
     "color":"#edb9bd",
     "shape":0,
     "icon":"f2d2",
     "localAllowed":true,
     "nodeTypeName":"Software Instance",
     "overlayKey":0,
     "overlayName":"",
     "overlayDescription":"",
     "overlayIcon":"",
     "overlayColor":"",
     "childName":"",
     "stamp":"2019-04-13T13:51:00",
     "author":"Nelly",
     "propertiesWithFieldDefinition":[

     ],
     "properties":[

     ],
     "zOrder":0,
     "location":"250.54142135623732 -0.09899494936611575"
  }

],
“linkDataArray”:[

]
}
\\

Hi

I have found a work around. Although this has not addressed the issue of the negative keys on nodes dragged from the palette onto the canvas when the canvas has a previous model restored as discussed before in this thread. The node data on the palette has a field call orgKey which stays stable. when handling the drag I use this value to re-set the node key to the correct value using the method setKeyForNodeData. This forces the node key back to the correct value after we have confirmed that a node with this key value does not already exist (although it did in a previous state _ see above thread). The code is as follows:

\\\\
function droppedFromPalatte(e) {
var droppedNode, existingNode, args;

// get dropped node
e.subject.each(function (node) {
    droppedNode = node;
});

if (droppedNode.data.key < 0) {
    existingNode = _baseDiag.findNodeForKey(droppedNode.data.orgKey);
    if (existingNode) {
        _baseDiag.remove(droppedNode);
        Animate(existingNode);
        return;
    }
    else
        _baseDiag.model.setKeyForNodeData(droppedNode.data, droppedNode.data.orgKey)
}

\\

Thanks for you help in this issue. Keen to know if this/was my issue or a GoJS issue.
Neil

I still cannot explain the behavior that you see. Here’s my test sample:

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <!-- Copyright 1998-2019 by Northwoods Software Corporation. -->
  <script src="go.js"></script>

  <script>
    function init() {
      var $ = go.GraphObject.make;

      // initialize main Diagram
      myDiagram =
        $(go.Diagram, "myDiagramDiv",
          {
            "animationManager.isEnabled": false,
            "ExternalObjectsDropped": function(e) {
               document.getElementById("myMessage").textContent = e.subject.first().key;
            }
          });

      myDiagram.nodeTemplate =
        $(go.Node, "Auto",
          new go.Binding("location", "location", go.Point.parse).makeTwoWay(go.Point.stringify),
          $(go.Shape,
            { fill: "white", stroke: "gray", strokeWidth: 2, portId: "" },
            new go.Binding("stroke", "color")),
          $(go.TextBlock,
            { margin: 6 },
            new go.Binding("text")),
        );

      // initialize Palette
      myPalette =
        $(go.Palette, "myPaletteDiv",
          { nodeTemplateMap: myDiagram.nodeTemplateMap });

      // now add the initial contents of the Palette
      myPalette.model.nodeDataArray = [
        { key: 24, text: "blue node", color: "blue" },
        { key: 25, text: "orange node", color: "orange" }
      ];

      // initialize Overview
      myOverview =
        $(go.Overview, "myOverviewDiv",
          { observed: myDiagram });

      load();
    }

    // save a model to and load a model from Json text, displayed below the Diagram
    function save() {
      var str = myDiagram.model.toJson();
      document.getElementById("mySavedModel").value = str;
    }
    function load() {
      var str = document.getElementById("mySavedModel").value;
      myDiagram.model = go.Model.fromJson(str);
    }
  </script>
</head>
<body onload="init()">
  <div style="width: 100%; display: flex; justify-content: space-between">
    <div style="display: flex; flex-direction: column; margin: 0 2px 0 0">
      <div id="myPaletteDiv" style="flex-grow: 1; width: 100px; background-color: whitesmoke; border: solid 1px black"></div>
      <div id="myOverviewDiv" style="margin: 2px 0 0 0; width: 100px; height: 100px; background-color: lightgray; border: solid 1px black"></div>
    </div>
    <div id="myDiagramDiv" style="flex-grow: 1; height: 400px; border: solid 1px black"></div>
  </div>
  <ol>
    <li>Drag the orange node from the palette to the main diagram -- new node's key will be 25</li>
    <li>Click the "Load" button to replace the model with what is parsed from the JSON shown below</li>
    <li>Again, drag the orange node from the palette to the main diagram -- and again the new node's key will be 25</li>
  </ol>
  
  <div id="buttons">
    <button id="loadModel" onclick="load()">Load</button>
    <button id="saveModel" onclick="save()">Save</button>
    dropped: <span id="myMessage"></span>
  </div>
  <textarea id="mySavedModel" style="width:100%;height:200px">
{ "class": "go.GraphLinksModel",
  "nodeDataArray": [
{"key":1, "text":"hello", "color":"green", "location":"0 0"},
{"key":2, "text":"world", "color":"red", "location":"70 0"}
  ],
  "linkDataArray": [
{"from":1, "to":2}
  ]}
  </textarea>
</body>
</html>

Note that I preset the orange node’s key in the Palette.model to be 25, as in your case. The “Load” button acts as an “undo” as long as you haven’t clicked “Save” or edited the text.

Hi

Thanks, I copied your code and ran it up - guess what? I get negative keys. First Orange -3 then Blue -4. This made me think of why it works for you and not for me, the possible difference is the version.

This version we are using is: GoJS v2.0.0 JavaScript Library for HTML Diagrams.

Yes that’s fixed it. Updated to 2.0.9 and your sample worked. On 2.0.0 your sample behaves how I said and delivers negative keys. Cannot see this fix in the ChangeLog, would not have picked it. I will update the main project and test tomorrow (AUS time). Thanks for helping with this.

It was a Model.fromJson compatibility bug fixed in 2.0.5, when loading a model written by version 1.*.

When checking for whether the new-to-2.0 Model.copiesKey property is set, it had been doing:

    if (!obj['copiesKey']) this.copiesKey = false;

But it should have been doing, and is now doing:

    if (obj['copiesKey'] === false) this.copiesKey = false;

The permissive JavaScript language strikes again.

I think you’re the first person to report this. Sorry about the hassle. Thanks for your patience.