Specify new node's group with ClickCreatingTool

I’m attempting to add a node to a group using the ClickCreatingTool and its archetypeNodeData property.

I’m creating a data object as below:

{
   category:"text-annotation",
   color:undefined,
   figure:undefined,
   group:"at1",
   isGroup:undefined,
   key:"Annotation",
   size:undefined,
   text:3,
}

However, when I create the new node and inspect it’s properties, group is undefined.

My template is as follows and is very simple:

    public createSimpleLabelNodeTemplate(): go.Node {
        return this.$go(go.Node, "Auto", 
        {
            locationSpot: go.Spot.Center,
            resizable: true
        },
        new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
        this.$go(go.TextBlock,
            {
                margin: 5,
                font: "bold 16px sans-serif",
                textAlign: "center",
                verticalAlignment: go.Spot.Center,
                stretch: go.GraphObject.Fill // make text occupy the space
            },
            new go.Binding("text"))
        );
    }

What am I doing incorrectly? Thanks!

Well, is there actually a Group in the Diagram whose .data.key == “at1”? And you are using a GraphLinksModel, aren’t you?

Shouldn’t data.text be a string rather than a number?

Yes, there is a group with that key; I am also using a GraphLinksModel.

I mean, I can do a .toString on data.text, but I’m meant to be displaying a numeric. It actually displays fine.

I initialize the model with other nodes referring to the group at1 and they work fine.

That’s because ClickCreatingTool.insertPart has to copy the ClickCreatingTool.archetypeNodeData to add to the model, and by default copying node data objects does not copy any group key.

I tested this out on a custom ClickCreatingTool:

  function InGroupClickCreatingTool() {
    go.ClickCreatingTool.call(this);
  }
  go.Diagram.inherit(InGroupClickCreatingTool, go.ClickCreatingTool);

  InGroupClickCreatingTool.prototype.canStart = function() {
    if (!this.isEnabled) return false;

    // gotta have some node data that can be copied
    if (this.archetypeNodeData === null) return false;

    var diagram = this.diagram;

    // heed IsReadOnly & AllowInsert
    if (diagram === null || diagram.isReadOnly || diagram.isModelReadOnly) return false;
    if (!diagram.allowInsert) return false;

    // only works with the left button
    if (!diagram.lastInput.left) return false;

    // the mouse down point needs to be near the mouse up point
    if (this.isBeyondDragSize()) return false;

    // maybe requires double-click; otherwise avoid accidental double-create
    if (this.isDoubleClick) {
      if (diagram.lastInput.clickCount === 1) this._firstPoint = diagram.lastInput.viewPoint.copy();
      if (diagram.lastInput.clickCount !== 2) return false;
      if (this.isBeyondDragSize(this._firstPoint)) return false;
    } else {
      if (diagram.lastInput.clickCount !== 1) return false;
    }

    // Allow ClickCreatingTool to work when (double?)clicking on nodes, including groups:

    //// don't include the following check when this tool is running modally
    //if (diagram.currentTool !== this) {
    //  // only operates in the background, not on some Part
    //  var part = diagram.findPartAt(diagram.lastInput.documentPoint, true);
    //  if (part !== null) return false;
    //}

    return true;
  };

  InGroupClickCreatingTool.prototype.insertPart = function(loc) {
    var model = this.diagram.model;  // assume it's a GraphLinksModel
    var oldcopies = model.copiesGroupKeyOfNodeData;
    // did the user (double?)click on a Group or on a Part that is a member of a Group?
    var part = this.diagram.findPartAt(loc, false);
    if (part !== null && !(part instanceof go.Group)) {
      part = part.containingGroup;
    }
    if (part instanceof go.Group) {  // clicked within a Group -- add the node to the group
      model.copiesGroupKeyOfNodeData = true;
      model.setGroupKeyForNodeData(this.archetypeNodeData, model.getKeyForNodeData(part.data));
    }
    var newnode = go.ClickCreatingTool.prototype.insertPart.call(this, loc);
    if (part instanceof go.Group) {
      model.setGroupKeyForNodeData(this.archetypeNodeData, undefined);
      model.copiesGroupKeyOfNodeData = oldcopies;
    }
    return newnode;
  };
  // end InGroupClickCreatingTool

I installed this tool in samples/basic.html via:

          // allow double-click in background to create a new node
          clickCreatingTool: new InGroupClickCreatingTool(),
          "clickCreatingTool.archetypeNodeData": { text: "Node", color: "white" },

Note that I had to override ClickCreatingTool.canStart so that it would operate when the user double-clicks on a Part.

Note also that I override ClickCreatingTool.insertPart not only to check if the click is on a Group, but also that if the user clicks on a Node or a Link, it checks if that Part is contained by a Group.

Interesting, thanks.

Is there a reason why the tool ignores the group key when its passed into via archetypeNodeData? This seems very confusing and probably should be clearly documented.

Well, imagine if GraphLinksModel.copiesGroupKeyOfNodeData were true. That would mean when the user copied some Nodes that were members of a Group, the copies would automatically be members of the original Group.

It’s only a problem because of copying and because ClickCreatingTool.archetypeNodeData is an archetype, not an actual node data object in a model.

Another way to look at it is that the copying code is responsible for determining the relationships. When copying a link data, for example, should the copied link refer to the original Link.fromNode and Link.toNode? Probably not.

Ah, I see. Thanks for the explanation!

Actually, I now notice that copiesGroupKeyOfNodeData is not documented.

You can avoid using that property by getting the new Node and setting Node.containingGroup to be the Group that was clicked, all in a “PartCreated” DiagramEvent listener. The custom ClickCreatingTool is still needed to allow the tool to start when clicking on a node.

Good call, thanks.