Selecting nodes: CTRL-A behaves different after CTRL-Z

Having the following error: Binding error: missing GraphObject named null in Node#11148

Although I cannot reproduce with a goJS sample, I noticed that my program behaves different when pressing the background before selecting with CTRL-A after using CTRL-Z. Maybe my problem is related how CTRL-A behaves.
I don’t receive the error when I press the background before selecting.

Using the angular sample.
Press CTLR-A, shows one selected node.
Drag copy the selected nodes.
Press CTRL-Z
Press CTRL-A, no node is selected

Now with pressing background:
Press CTLR-A, shows one selected node.
Drag copy the selected nodes.
Press CTRL-Z
Press Background
Press CTLR-A, a node is selected.

I’m sorry, but I cannot reproduce any problem, even when using Minimal GoJS Sample with AngularJS.

So I cannot even reproduce what you did in your middle paragraph. Ctrl-A invokes the CommandHandler.selectAll command, which would normally select all of the nodes and all of the links. Even if I delete all of the nodes but one, so that Ctrl-A only shows one selected node, drag-copying it to produce a second node and Ctrl-Z to undo leaves only one node. Ctrl-A at that time does select that one node.

Sorry to be unprecise.
The node is graphically selected but the ChangedSelection handler seems not to have a valid selection, no nodes are in the selection.

          diagram.addDiagramListener("ChangedSelection", function(e) {
            var selnode = diagram.selection.first();
            diagram.model.selectedNodeData = (selnode instanceof go.Node ? selnode.data : null);
            if (diagram.model.selectedNodeData === null) {
                console.log("nothing selected");
            } else {
                console.log("selected " + diagram.model.selectedNodeData.name);
            }
            scope.$apply();
          });

In console log
Press CTRL-A: selected Alpha
Drag copy: selected Alpha
CTRL-Z: nothing selected
CTRL-A: nothing selected (in my eyes wrong)

You’re right that there’s something wrong in that sample. The first/original Node that you see really is selected and in the Diagram.selection collection. However, that state is not reflected in the value of $scope.model.selectedNodeData, which is still referring to the second copied-and-now-deleted-by-undo node data.

I’ll look into it.

The problem is that selected Parts can remain selected (i.e. Part.isSelected is true and it is in the Diagram.selection) even when the part no longer “exists” because of an undo of an insertion of a new selected Part.

Keeping the selection state independent of being in the diagram is convenient when using undo and redo, so that the user can keep track of what has been happening with selected parts. I suppose we could change the CommandHandler.selectAll command to make sure there aren’t any non-existing parts in the resulting Diagram.selection collection, but that would not avoid the situation that you described. Instead of using Ctrl-A, the user might shift-click or control-click to select one or more of the remaining parts. That would still leave the at-the-moment-not-in-the-diagram parts in the selection collection.

I’m not sure what you want in your Angular model when a selected part is deleted, at any given moment. Here’s one possibility:

          // update the Angular model when the Diagram.selection changes
          function updateSelection(e) {
            diagram.model.selectedNodeData = null;
            var it = diagram.selection.iterator;
            while (it.next()) {
              var selnode = it.value;
              // ignore a selected link or a deleted node
              if (selnode instanceof go.Node && selnode.data !== null) {
                diagram.model.selectedNodeData = selnode.data;
                break;
              }
            }
            scope.$apply();
          }

By the way, not directly related to this topic, but something you might want to know: ChangedSelection is not triggered - #2 by walter

As I see it, selecting the new copied parts is part of a transaction, or should be within a transaction. Therefor deselecting the new parts, removing the parts and selecting the original parts should be within an undo.

Otherwise the following happens:

          // update the Angular model when the Diagram.selection changes
         diagram.addDiagramListener("ChangedSelection", function(e) {
            diagram.model.selectedNodeData = null;
            console.log("selected count: " + diagram.selection.count);
            var it = diagram.selection.iterator;
            while (it.next()) {
              var selnode = it.value;
              // ignore a selected link or a deleted node
              if (selnode instanceof go.Node && selnode.data !== null) {
                diagram.model.selectedNodeData = selnode.data;
                break;
              }
            }
            scope.$apply();
          });

CTRL-A: selected count: 9, key=1 selected
drag copy: new parts are copied, selected count: 9, key=-1 selected (selecting the new parts should be in the transaction, also deselecting the original parts should be in the transaction)
CTRL-Z: selected count: 9, no node selected, as we iterate over the deleted parts (in my eyes wrong, when we reverse the previous transaction: deselect new parts, remove them, select the original parts)
CTRL-A: selected count: 18, key=1 selected
drag copy: new parts are copied, selected count: 13, key=-5 selected
CTRL-Z: selected count: 13, no node selected
CTRL-A: selected count: 22, key=1 selected
drag copy: new parts are copied, selected count: 13, key=-5 selected, by now I see artifacts on the diagram. (In my implementation resulting in some binding problems.)

In my eyes, keeping the deleted non-existing nodes selected makes no applicatory sense. When someone would need the information he has to programmaticaly save the parts on the change select event. So after a CTRL-Z he can act on them. For the “normal” programs to code around this in my eyes “misbehaviour” is cumbersome and someone needs to now the goJS internals.

Also, now the user looses his selection after an undo and might need to tediously shift-click, control-click again.

That the de-selecting of the old and re-selecting of the original parts should be part of the transaction is easily shown how Word behaves:
Write some characters, select a part of the written text, CTRL-C, CTRL-V, CTRL-V, CTRL-Y, CTRL-Y, the in the beginning selected parts are re-selected again.

As a quick win, due to the fact re-selecting the original nodes is not part of a reverse-transaction during an undo, the Diagram.ClearSelection could be called at the end of an undo.

This design topic deserves some further investigation. However I am reluctant to make such changes in 1.6, so I hope that my suggested work-around is sufficient for you to make progress with your app.

In 1.7 we have improved the behavior involving selection or highlightedness for parts that were inserted and then removed upon an undo. Here’s the change log entry:

Parts that are added and selected or highlighted, and then are removed upon an undo, no longer remain selected or highlighted.
This policy avoids having non-existent nodes and links (due to undo of their insertion) remaining in the Diagram.selection collection.

Great, would love to test this, however 1.7 seems not yet downloadable. The changelog still has 1.6.21: Download GoJS

When is the 1.7 available for download?

It’s still in “alpha” test phase, but you can look at GoJS - Build Interactive Diagrams for the Web.