Is it possible to add any gojs shape on the edges of tree layout group for every node with in that group

I have a dynamically constructed tree layout group.My requirement is to add a gojs shape on the edge of the group on either of the sides.How can we do it in go js.I tried adding the shape object with alignment as go.Spot.Left. But the shape appeared after the treelayout’s parent-child link.But I need to get the shape on the edges exactly(either left or right).

Below screenshot is the expected scenario.

This is the existing scenario.

You may need to take the entire group and place it inside another panel.

In other words the old structure was:

Group (Panel)
    Group's contents

And the new structure:

Containing Panel (that is a Group and a Spot Panel)
    Panel (that used to be the Group)
        Panel's contents (the old group's contents)
    Object that is a sibling of the Panel

Read about panels generally: GoJS Panels -- Northwoods Software

Hi Simon,

This is our existing implementation:

This is the expected implementation:
https://forum.nwoods.com/uploads/db3963/original/2X/9/99027558bf5fce86640e737c293ac9a0f4354e23.PNG

Now we want a couple of things here:

  1. For every tree node in the first image, we want a green triangular shape on the edge of the group. This shape will be the one from which the link for that node will originate. For the left side Tree structure, the shape needs to be on the right edge and on the right tree we want the shape on the left edge.

  2. Irrespective of the hierarchy, that green triangular shape needs to be on the edge always. Also, since we have a port id associated with every treenode, we have to somehow bind the portid with that shape, so that in the linkdataarray we can store that information of the link (fromNode and ToNode) without disturbing the tree shape.Currently when we are binding the same port id with the port id of the shape, then the tree hierarchical structure is disturbed (The parent node creates link to the shape and to the child node as well).

As I believe we have told you before via email, with the approach that I believe you are taking (each item is a Node in a tree within a Group, laid out with a TreeLayout) you are not going to be able to have scrolling/clipping. And I think it will require customizing the TreeLayout in order to have it understand that the ports should be far away from the nodes, at varying distances.

I believe that you could implement what I am guessing that you want by using a single Node and an array of items, one per row, as demonstrated in Scrolling Table. Adding a port per row, centered on the edge of the whole node body, should be relatively easy. However that sample does not demonstrate indenting items as part of a simulated tree structure, nor does it demonstrate collapsing/expanding subtrees. Those tree-related features would need to be implemented from scratch.

We can help you with this latter task.

Hi Walter,
Thanks a lot for your inputs.
We tried that way customizing the TreeLayout . We are very near to our requirement but facing some alignment issues in two things.

  1. Alignment of that “arrow” shape object based on the group object
  2. Alignment of the nodes in the tree( indentation)

I believe what I am trying to do is right codewise, but I feel like "alignment" property is not working in my code.
Can you help me, if I am missing something or any other property is overriding that one in the above mentioned two places.

Note: Even if I am giving alignment as go.Spot.Right by default for both the group objects instead of binding it based on the group, the arrow object is taking left position only.

Attaching the screenshots of the current scenario:

Here is the nodeTemplate and TreeLayout custom function we are using:

                var nodeTemplate =
                                    $(go.Node, {
                                            fromLinkable : false,
                                            toLinkable : false
                                        },
                                        $(go.Shape, "RoundedRectangle", {
                                            width: 260,
                                            // stretch: go.GraphObject.Horizontal,
                                            height: 25
                                        }),
                                        $(go.Panel, "Horizontal",
                                            {
                                                name: "indentPanel",
                                                // alignment: go.Spot.Left, // This spot gets modified by the layout to be more indented
                                                // alignmentFocus: go.Spot.Left,
                                                margin: new go.Margin(0,0,0,15)
                                            },
                                            $("TreeExpanderButton", {
                                                    width: 14,
                                                    height: 14 
                                            }),
                                            $(go.Panel, "Horizontal",
                                                $(go.TextBlock, {
                                                        stroke: "#333333",
                                                        margin: 2,
                                                    },
                                                    new go.Binding("text", "name")))
                                            ),
                                            // The arrow that acts as a port on either the left or ride side
                                            $(go.Shape,
                                                {
                                                    figure: "TriangleRight",
                                                    margin: new go.Margin(8,0,0,0),
                                                    width: 8.5, height: 8.5,
                                                    fill: "white",
                                                    stroke: "lightgray",
                                                    strokeWidth: 1
                                                },
                                                new go.Binding("portId", "portId").ofObject(),
                                                new go.Binding("alignment", "containingGroup", function (group) {
                                                    if (group !== null && group.data.key.includes("group2")) return go.Spot.Left;
                                                    return go.Spot.Right;
                                                }).ofObject(),
                                                new go.Binding("alignmentFocus", "containingGroup", function (group) {
                                                    if (group !== null && group.data.key.includes("group2")) return go.Spot.Left;
                                                    return go.Spot.Right;
                                                }).ofObject(),
                                                new go.Binding("fromLinkable", "containingGroup", function (group) {
                                                    return !(group !== null && group.data.key.includes("group2"));
                                                }).ofObject(),
                                                new go.Binding("toLinkable", "containingGroup", function (group) {
                                                    return (group !== null && group.data.key.includes("group2"));
                                                }).ofObject()
                                            )
                                        );


        function TreeLayout() {
             go.TreeLayout.call(this);
         }

        go.Diagram.inherit(TreeLayout, go.TreeLayout);

        TreeLayout.prototype.commitNodes = function () {
            go.TreeLayout.prototype.commitNodes.call(this);
            var vit = this.network.vertexes.iterator;
            while (vit.next()) {
                var v = vit.value;
                var indentPanel = v.node.findObject("indentPanel");

                var indent = (v.level * 26) + 20;
                if (indentPanel !== null) indentPanel.alignment = new go.Spot(0, 0.8, indent, 0);

            }
        };

I think you are basically doing the right things, but instead of changing the alignment , I would add a transparent Shape as the first thing in that Horizontal Panel, and I would change its width in order to achieve indenting.

And instead of having the Node be a Position Panel (the default type of Panel), I would remove the RoundedRectangle Shape and make the whole Node the Horizontal Panel and specify a minSize.

Using a Horizontal Panel around a TextBlock as its only element is inefficient too – you don’t need a Panel there – just use the TextBlock.

Hi,
I am able to align the arrow shape objects as per my requirement.
But adding a specific width to the Rectangle shape object within a panel of the groupTemplate is affecting the co-ordinates of the diagram. I don’t understand if it’s actually the co-ordinates of the nodes that is changing or the diagram itself expanding right-side, which looks like all the nodes X-coordinates taking some fixed decremental value. This is happening for every change in the model (Either adding a node using addNodeData method or reassign of model using go.GraphLinksModel).

Can you please help me to figure out what is the cause for that issue and solution for that?

var defaultGroupTemplate =
                        $(go.Group, "Auto", {
                                selectionAdorned: false,
                                name: "objectshape",
                                copyable: false,
                                deletable: false,
                                resizable: false,
                                layout: $(go.TreeLayout, {
                                    alignment: go.TreeLayout.AlignmentStart,
                                    angle: 0,
                                    compaction: go.TreeLayout.CompactionNone,
                                    layerSpacing: 0.8,
                                    layerSpacingParentOverlap: 1,
                                    nodeIndent: 2,
                                    nodeIndentPastParent: 0.99,
                                    nodeSpacing: 0,
                                    setsPortSpot: false,
                                    setsChildPortSpot: false
                                })
                            },
                            new go.Binding("position", "xy", go.Point.parse).makeTwoWay(go.Point.stringify),
                            $(go.Panel, "Auto",
                                $(go.Shape, "RoundedRectangle", {
                                    fill: colors.white,
                                    stroke: properties.borderStroke,
                                    width: 250
                                }),
                                $(go.Panel, "Vertical", {
                                        defaultAlignment: go.Spot.Left
                                    },
                                    getHeaderTemplate(properties.headerHeight),
                                    $(go.Placeholder, {
                                        padding: 5
                                    })
                                )
                            )
                        );

Are you saying that the x value of Diagram.documentBounds decreases each time you replace Diagram.model, without the x values of the positions of the member Nodes also decreasing?

Is the node template modified at all from what you showed above?

Exactly!
The x value of Diagram.documentBounds is decreasing each time I replace Diagram.model, without the x values of the positions of nodes decreasing.

The nodeTemplate is not at all modified.

The only thing causing this issue is adding that width property to the Rectangle shape object within a panel of the groupTemplate.

For suppose, A fixed position is given for a node, say, “400 500”. The value of the node position remaining same, yet falling leftwards of the diagram for every change in the model.

I have no idea of what you are doing that might cause that problem. It sounds as if whatever modifications you are making to some Shape within Groups is somehow extending itself beyond the lifetime of one diagram/model. Do you have any state whose lifetime extends beyond the lifetime of a Model?

Sorry, I don’t understand you. Can I know what you mean by the lifetime of a Model?

Do you have any global variables whose value changes? Especially between the time the first time that you set Diagram.model and the next time that you do.

No, I am not using any. But I am populating different data to the model object for different div elements using the same diagram template.

A template is always copied for each node or link in the model, so modifying a Node or a Link cannot affect any other existing or future Nodes or Links.

But it is possible to modify the template itself, although you must do so carefully. I hope none of your code is doing so.