Link Location in Tree Layout

Hi,
My Diagram has Tree Layout with the following attributes :
this.diagram.layout = this.GO(go.TreeLayout,
{ angle: 90, nodeSpacing: 10, layerSpacing: 40, layerStyle: go.TreeLayout.LayerUniform, alignment: go.TreeLayout.AlignmentCenterChildren, childPortSpot : go.Spot.TopCenter } );

Some of the child nodes are groups.
When a group is collapsed, the link from the parent node to the group node is located on the left side of the group, as shown in the picture :

When the group is expanded, the link is located on top of the group, as shown in the picture :

I would like the link to be always connected on the top of the group.
How can i force it ?

I’m unable to reproduce this behavior. Could you help us reproduce the problem?

You say that the child is a group. Does the link’s model data actually connect with the group or with a member node of the group?

The link is connected to the group node, not to one of its member nodes.
I noticed that for of the groups, before they are expanded, the link is on the side of them, when expanded, the link goes top, and when re-collapsed, the link it maintained on the top of them.
It does not happen for all groups, but for only some of them.
It looks like some algorithm is deciding where to put the link.

OK. So the Groups start off collapsed? Also, do your Groups themselves have a Group.layout? Could you provide your Group template.

Actually, in looking at your first screenshot, I don’t understand how that group node was positioned so far to the right of any other node. Your Diagram.layout’s properties, in particular TreeLayout.nodeSpacing, would not produce such a layout.

When the TreeLayout.angle is 90, the Link.toSpot is set to go.Spot.TopCenter, so your setting of TreeLayout.childPortSpot is redundant. You can try removing it to make sure that that is the case. You were probably trying it just to see if it helped.

The Group has no group layout.
The screen shot taken after i moved to child node to the right.
I also deleted the childPortSpot attribute.
Didn’t help.
Do you still need my layout ?

Found the problem.
My Group layout has a dedicated “Circle” go.Shape with height:0. It is the first element in a “Spot” panel.
This Shape serves the link connection between group to go behind all elements.
Any way to eliminate the Shape for some of the groups ?

Could you look in the debugger at the Link that connects to the child/Group? I’m wondering why its Link.toSpot isn’t Top.

You can do it by selecting that Link and evaluating something like:

myDiagram.selection.first().toSpot.toString()

It should return “Spot(0.5,0)”.

Once i invisible the “Circle” Shape mentioned before, the link connects to the top ,of the group, yet it is in front of all elements, i need to push the link “behind” the group.
Tried zOrder the link to -100.
Didn’t help.

Sorry, we cross-posted, so I didn’t read your post mentioning a “Circle” Shape.

I don’t understand the purpose of that “Circle” Shape. Is its GraphObject.portId set to the empty string?

All of the visual tree of a Group is drawn with the same z-ordering as the Group itself. Each of the members of the Group can have a z-ordering different from the Group – they are independent Parts.

yes this Shape has portId: “”.
I tried to set the Link zOrder to -100, but it didnt send the link behind the group

Part.zOrder only controls the ordering within a Layer. What layers are the Link and the Group in? If they are in the same layer, what is the Group’s zOrder?

I set group zOrder to 1 and the link zOrder to 0 and the link went behind. Now i need to eliminate the “Circle” Shape from the template, namely delete it when running the TreeLayout.
Any way to delete an object (Shape) within on object ?
I tried
obj = group.findObject(“linkConnector”)
groupt.remove(obj);

Didn’t work

Would you mind sharing your group template? That may help us better understand how to simplify your groups to achieve what you want. What happens if you just remove the “Circle” shape from the template?

If i remove the “Circle” object from the template, it works fine.
Here is the Group template :

defaultGroupTemplate(that) {
    var GO = that.GO;
    var x =
      GO(go.Group, "Auto",   // Header and group members panel one below each other
        {
          isSubGraphExpanded: false,
          ungroupable: true,
          background: "white",
          mouseOver: function (e, node) { node.isShadowed = true; },
          mouseLeave: function (e, node) { node.isShadowed = node.isSelected; },
          doubleClick: function (e, node) { that.diagram.commandHandler.expandSubGraph(node); },
          toolTip: that.siteTooltip(GO)
        },
        new go.Binding("scale", "groupPictureScale").makeTwoWay(),
        new go.Binding("position", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
        new go.Binding("isSubGraphExpanded").makeTwoWay(),
        new go.Binding("isShadowed", "isSelected", function (s) { return s; }).ofObject(),

        GO(go.Shape, "RoundedRectangle",
          {
            fill: "transparent",
            isPanelMain: true,
            stroke: that.CONST_GROUP_BORDER_COLOR,
            strokeWidth: CONST_GROUP_BORDER_WIDTH
          },
            new go.Binding("stroke", "statusColor"),
           new go.Binding("opacity", "groupMonitoringOpacity"),
        ),

        // Panel for holding COLLAPSED Group Header info
        GO(go.Panel, "Vertical",
          GO(go.Panel, "Table",
            {
              stretch: go.GraphObject.Horizontal,
              background: "#124386",
            },

            //  Panel for implementing our own  custom SubGraph Expander Button
            GO(go.Panel, "Spot",
              { column: 0, },
              new go.Binding("visible", "isSubGraphExpanded", function (s) { return s; }).ofObject(),
              GO(go.Shape, "Rectangle",
                {
                  desiredSize: new go.Size(34, 34),
                  fill: "#124386",
                  //stroke: "white",
                  stroke: that.CONST_GROUP_BORDER_COLOR,
                  click: function (e, node) { that.diagram.commandHandler.collapseSubGraph(node); }
                },
              ),
              GO(go.Shape, "Rectangle",
                {
                  desiredSize: new go.Size(10, 2),
                  fill: "white",
                  stroke: "transparent",
                  alignment: go.Spot.Center,
                  click: function (e, node) { that.diagram.commandHandler.collapseSubGraph(node); }
                },
              )
            ),
            // group name in Header
            GO(go.TextBlock,
              {
                column: 1,
                font: "bold 14pt rubikregular",
                editable: false,
                stroke: "white",
                margin: 2,
                verticalAlignment: go.Spot.Center,
                textAlign: "center",
                stretch: go.GraphObject.Horizontal,
              },
              new go.Binding("text", "groupName").makeTwoWay(),
              new go.Binding("editable", "isAutomaticGroup", function (val) { return !val; }),
            ),
            new go.Binding("stroke", "statusColor")
          ),  // Horizontal Panel

          // Panel for Holding Group Info (name, icon, site) and place holder
          GO(go.Panel, "Auto",
            { width: 100, height: 150 },
            new go.Binding("visible", "isSubGraphExpanded", function (s) { return !s; }).ofObject(),
            GO(go.Panel, "Spot",
              // a Dedicated "hidden" shape (height=0) to enable connection between collapsed group links.
              GO(go.Shape, "Circle",
                {
                  height: 0,
                  portId: "",
                  stretch: go.GraphObject.Horizontal,
                  name: "LinkConnectorShape"
                }
              ),
              // The portId serves as the link connector
              GO(go.Panel, "Vertical",
                {
                  alignment: go.Spot.Top,
                  minSize: new go.Size(90, 80)
                },
                // Group Icon
                GO(go.Picture, {
                  scale: 0.9,
                  margin: new go.Margin(20, 0, 0, 0)
                },
                  new go.Binding("source"),
                ),
                // operAssociation - שיוך מבצעי
                GO(go.TextBlock,
                  {
                    font: that.CONST_DEFAULT_FONT,
                    editable: false,
                    wrap: go.TextBlock.WrapFit,
                    textAlign: "center",
                    width: 90,
                    margin: new go.Margin(10, 0, 0, 0)
                  },
                  new go.Binding("text", "operAssociation"),
                  new go.Binding("visible", "operAssociation", function (s) { return (s != null && s != ''); }),
                ),
                // group address
                GO(go.TextBlock,
                  {
                    font: that.CONST_DEFAULT_FONT,
                    editable: false,
                    wrap: go.TextBlock.WrapFit,
                    textAlign: "center",
                    width: 90
                  },
                  new go.Binding("text", "address"),//IDO changed from groupName
                  new go.Binding("visible", "address", function (s) { return (s != null && s != ''); }),
                ),
                // Quality Message
                GO(go.TextBlock,
                  {
                    font: that.CONST_DEFAULT_FONT,
                    editable: false,
                  },
                  // if mobile and has modem with rx quality - show in group
                  new go.Binding("visible", "siteMobilityCode", function (siteMobilityCode, obj) {
                    if (obj["part"].data.networkType == undefined)  // show site type for site group only
                      return (siteMobilityCode != null && siteMobilityCode == 1);
                    else
                      return false;
                  }),
                  new go.Binding("text", "rxQUalityMsg"),
                  new go.Binding("stroke", "rxTextColor")
                ),
              ),
            ),
            // if mobile and has modem with rx quality - show rxquality icon in group
            GO(go.Picture, {
              scale: 1.4,
              alignment: go.Spot.TopRight,
            },
              new go.Binding("visible", "siteMobilityCode", function (siteMobilityCode, obj) {
                if (obj["part"].data.networkType == undefined)  // show site type for site group only
                  return (siteMobilityCode != null && siteMobilityCode == 1);
                else
                  return false;
              }),
              new go.Binding("source", "rxQUalityIcon", function (q) {
                let icon = "";
                var qualityString = ['', 'week', 'poor', 'good', 'best'];
                if (q > 0)
                  icon = 'rx_quality_' + qualityString[q] + '.svg';
                return NetMapDiagramComponent.CONST_ICON_RELATIVE_PATH + icon;
              })
            ),
            // Site Status -
            // (show this textbox only if site status is NOT KAYAM)
            GO(go.TextBlock,
              {
                visible: false,
                background: 'rgba(0,0,0,0.4)',
                height: 25, width: 100,
                textAlign: 'center',
                font: "bold 13pt rubikregular",
                stroke: '#fff',
                verticalAlignment: go.Spot.Center
              },
              new go.Binding("visible", "statusCode", function (statusCode, obj) {
                // if the group type if of NetworkType or User, dont show the status (visible=false)
                if (obj["part"].data.networkType != undefined || statusCode == undefined)
                  return false;
                else
                  // the group is of site with site status code. Show the status only of statusCode NOT KAYAM(0)
                  return statusCode != 0;
              }
              ),
              new go.Binding("text", "statusCode", function (statusCode, obj) {
                // show the value of group type of Site only and only is statusCode exists
                if (obj["part"].data.networkType == undefined && statusCode != undefined) {
                  var statusString = ['קיים', 'בפירוק', 'בהקמה', 'בדילוג', 'לא פעיל'];
                  return statusString[statusCode];
                }
              })
            )  // textblock
          ),
          // Panel for holding Group members
          GO(go.Panel, "Auto",
            new go.Binding("visible", "isSubGraphExpanded", function (s) { return s; }).ofObject(),
            GO(go.Placeholder, // represents area for all member parts
              { background: "transparent", margin: 10 },
            )  // Place holder
          )  // Auto
        ) // Auto
      );
    return x;
  }

If you are going to delete the apparently unnecessary “Circle” Shape, you should delete the “Auto” Panel that is using that shape as the border around the rest of the elements of that panel.

In general there should not be any “Auto” or “Spot” Panels that have zero or only one element in them – they should have two or more elements. I notice that you have an “Auto” Panel surrounding only a Placeholder – you can probably remove that “Auto” Panel and leave just the Placeholder.

Ok, Understood.
So, how can I delete the “Circle” Shape ?
It pushes the link behind the group. I want the link to connect to the Top of the group node.

Please format your code by surrounding your block of code with lines consisting of triple-backquote: Code Formatting

Even when your code is formatted, it’s too long for me to understand its structure without taking a lot of time to read it.

Basically, if your structure is:

. . .
  Panel
    Panel, "Spot"
        Shape, "Circle"
        (any element)

you can remove the Circle Shape by removing that Shape and its containing “Spot” Panel:

. . .
    Panel,
        (any element)

You will probably want to move any properties and Bindings that were on the excised “Spot” Panel to the remaining (any element).

I’m not sure i’m clear.
The “Circle” shape is contained in an “Auto” panel.
I cannot remove the “Auto” panel since it has lots of other panels.
Maybe i can create a “dummy” “Auto” panel and place the “circle” in it, and then, delete the “Auto” shape…

What is the purpose of that particular “Spot” Panel if it no longer has its main element (the zero height Circle Shape)?

If you say just deleting the Circle Shape leaves the whole Group working well, why are we having this conversation?

I’m using this template for network diagram that connects telecom links and nodes.
I’m implementing a function that transform the network layout into tree layout.
When i go into tree layout, i need to “hang” the group node under the a parent node but maintain its template.
So, in order to hang the group node properly , i would like to delete the “Circle” shape.