Saving the node position/location when creating a group

A Group with a Placeholder goes wherever the Placeholder goes, which in turn goes wherever the member Nodes are.

Are you sure you have no member nodes near the origin? And that all of the member nodes have real number values for the location?

Regarding the “origion”, i’m not sure i understand.
I have a nodeDataArray with 4 nodes. When i press a button i create 2 groups, one with 3 nodes and one with one node.
When the group is created i give its member nodes “loc” value with X value which increment by 50 and Y=0. As result the group members are lied down in Horizontal manner (X,Y) = (0,0), (50,0), (100,0).
Strange, when i print the (X,Y) to the console i see (5.68, -2.84), (50,-5.68), (99.99, -5.68) Respectively.
When i set the Y value to 200, (X,Y) = (0,200), (50,200), (100,200), when the group for the first time, the group jumps down to Y=200.
How can i force the group node to retain its position and to expand to the max X&Y value of its member nodes ?

By origin I meant the point (0,0) in document coordinates.

When you create a group with member nodes at (0,0), (50,0), and (100,0), then it makes sense that the group will have a location near the top-left corner of the union of the member nodes’ actualBounds.

The same goes for when the member nodes have Y==200 – the group will be farther down in the Y direction. You can check that by also printing the Group.location, but you are saying that the location is indeed near Y==200.

So it should be the case that the width and height of the group will encompass the bottom-right corner of the union of the bounds of the member nodes. And from what you just said, it appears that the group is located correctly.

It looks like we will not end the discussion forever,
I guess i don’t understand something.
I’m just curios, how is in your grouping sample, when a group is expanded for the first time, it retains its position and in my code, it moves to some position.
What code should i write or avoid in order to retain the group position when expanded for the first time ?

If you have an existing collapsed group with no member nodes, and then you want to add some member nodes, you should add them so that the nodes are to the right and/or below the Group.location. If your Placeholder has a Placeholder.padding, you’ll need to account for that margin too.

Then when the user expands the group, the group will stay in the same place because the Placeholder remains in the same place.

On the other hand, if you start with a bunch of existing nodes, you select them, and then you call CommandHandler.groupSelection (assuming it’s been enabled), the new group will naturally remain where you would expect because the member nodes have not moved, so the Placeholder stays where those member nodes are.

The use case i’m referring is the second one. I have a bunch of nodes and when a button is pressed, i create a group node and add the nodes to the group.
these are my templates :
function defaultNodeTemplate() {
var $ = GO;
var x =
$(go.Node, “Auto”,
$(go.TextBlock,
{ margin: 7, font: “Bold 14px Sans-Serif” },
new go.Binding(“text”, “shortId”))
);
return x;
}

function  defaultGroupTemplate() {
    var $ = GO;
    var x =
        $(go.Group, "Auto",
            {
                isSubGraphExpanded: false,
            },
          //   new go.Binding("position", "loc", go.Point.parse),
            $(go.Shape, "Rectangle",
                {fill: null, stroke: "gray", strokeWidth: 2}),
            $(go.Panel, "Vertical",
                {defaultAlignment: go.Spot.Left, margin: 4},
                $(go.Panel, "Horizontal",
                    {defaultAlignment: go.Spot.Top},
                    $("SubGraphExpanderButton"),
                    $(go.TextBlock,
                        {font: "Bold 18px Sans-Serif", margin: 4},
                        new go.Binding("text", "key"))
                 ),
                $(go.Placeholder,
                     {padding: new go.Margin(0, 10)}
                )

) // end Vertical Panel
); // end
    return x;
}

And this is the grouping Function :

function groupNodes() {
    //=================================================================
    diagram.startTransaction("groupNodes");
    var keyHashMap = {};
    var newGroupArray = [];

    for (i = 0; i < diagram.model.nodeDataArray.length; i++) {
        var nodeData = diagram.model.nodeDataArray[i];
        var groupKey = nodeData.site.siteId; // site.siteId;
        if (keyHashMap.hasOwnProperty(groupKey) === false) {
            keyHashMap[groupKey] = true;
            var groupData = {
                key: nodeData.site.siteId,
                isGroup: true,
                isExpanded: false,
            };
            xg += 500;
            newGroupArray.push(groupData);
        }
        diagram.model.setDataProperty(nodeData, "group", groupKey);

    }
    // Add the group nodes to the data model
    for (i = 0; i < newGroupArray.length; i++) {
        diagram.model.addNodeData(newGroupArray[i]);
    }
    diagram.alignDocument(go.Spot.Center, go.Spot.Center);
    diagram.commitTransaction("groupNodes");

}

Where have i go wrong ?

Is there a reason you aren’t calling CommandHandler.groupSelection?

Usually i group by a key called siteId and not by a selecting nodes to be grouped.
Namely, the user presses a button ‘group’ and the algorithm should distribute the nodes by siteId into groups, a group per each siteId

OK, here’s a sample that demonstrates a number of the topics we’ve discussed: Page Not Found -- Northwoods Software

I have no idea how much of this matches up with your expectations; probably some can be achieved by setting the right properties.

Read the code and the comments for more explanations.

Thanks,
I used your code and i realized the big difference from my code to your code:

  1. You use auto layout. My code does not;
  2. You create nodes without “location” (because you use layout). My nodes have “location”.
  3. Your created group is always “expanded”. Mine is collapse.

I took your code and made the following changes (according to my projects requirements):

  1. Commented the layout for diagram and group.
  2. created nodes with “location”
  3. Set the group template to isSubGraphExpanded: false.

Voila, your code behaves like mine. Namely all group are created and positioned somewhere on the view port (didn’t set their “location”).
As soon as a group is expanded it “moves” according to its node members location and from that point it collapses and expanded to the same place.
So, i guess that setting the nodes location in the group and the fact that the group is not expanded causes the group to “move” to its desired position, its nodes position, when expanded for the first time.
I will have to live with it.

Ah, if the group starts off collapsed, then you need to assign the Group.location yourself, because there are no visible member nodes to give the Group.placeholder any size or position.

Whether one uses a layout to assign node locations or not probably doesn’t matter if the group starts off expanded, which is the default behavior.

So,
I guess there is no way to set and retain the group location if i set the nodes location…

Sure you can – just make sure you assign the group’s location to what it would be if it were expanded.

I used your code and set one of the group location (the “lightblue”) to location=“700 700”.
The group location did created on “700 700” but as soon as it was expanded it moved to its nodes location.
No news…
I thought that node with location (W,Z) when added to a group with location (X,Y), the node gets location (X+W,Y+Z).
No ?

No, all Parts have sizes and positions that are in document coordinates. In other words, member Nodes do not have locations that are relative to their containing Group’s location.

If you want to move any node, especially groups with their member nodes, call Part.move or Part.moveTo.

OK,
Thanks

In order to prevent the group to move to some location when expanded for the first time,
i changed the groupTemplate to :
isSubGraphExpanded = true;
and added the following code right after the group is added to the model :

setTimeout(function(){
var g = diagram.findNodeForKey(groupKey);
g.isSubGraphExpanded = false;
},
0);

Now, when the new group is expanded it retains its position.

OK. That should not be necessary, but I could imagine that it’s easier to implement asynchronously.

You could collapse some or all the groups at once in a setTimeout called in an “InitialLayoutCompleted” DiagramEvent listener.

OK, i will try.
Is there any fast way to collapse all groups ?

It’s presumably the same amount of time no matter how you do it.

But the easy way to write the code to collapse them all (which earlier I didn’t know that you wanted to do) would be something like:

myDiagram.commit(function(d) {
  d.findTopLevelGroups().each(function(g) { g.collapseSubGraph(); });
}, "collapse all groups");