Custom template for group + node

Hello, I would like to create a custom template for group + node type on my diagram.
It’s like a hierarchy view to manage for example a Database > Database Table > Table field 1 / Table field 2 etc.

Below, the template I want to create.

I can have link between :

  • Group to inner node
  • Group to other group
  • node to other node
  • Node to group

I nearly succeed in doing the template but I have strange behaviour sometimes with links and padding between groups or nodes.
1_error_endOfArrow paddingAndLink

Below I copy/paste the code for nodes and links.

//Template for node
        diagram.nodeTemplate =
            $(go.Node, "Auto", {
                    movable: false,
                    copyable: false,
                    avoidable: true,
                    click: function (e, node) {
                        if (node instanceof go.Node) {
                            highlightLinks(node);
                        }
                    }
                },
                $(go.Panel, go.Part.Table, {stretch: go.GraphObject.Fill},
                    $(go.Panel, go.Panel.TableRow, {alignment: go.Spot.Right},
                        $(go.Picture, {
                            column: 1,
                            margin: new go.Margin(2, 2, 2, 15),
                            width: 16,
                            height: 16
                        }, new go.Binding("source", "source")),
                        $(go.TextBlock,
                            {column: 2, margin: 5, editable: false,},
                            new go.Binding('text').makeTwoWay()
                        )
                    )
                )
            )
//Template for group
        diagram.groupTemplate =
            $(go.Group, "Auto",
                {
                    layout: $(go.TreeLayout, {
                        angle: 90,
                        alignment: go.TreeLayout.AlignmentStart,
                        layerSpacing: 10,
                        nodeSpacing: 20,
                        portSpot: go.Spot.Left,
                        childPortSpot: go.Spot.Left,
                        sorting:go.TreeLayout.SortingAscending,
                        comparer: function(a:go.TreeVertex,b:go.TreeVertex){return nodeSorting(a,b)}
                    }),
                   
                    avoidable: false,
                    isShadowed: false,
                    shadowOffset: new go.Point(3, 3),
                    shadowColor: "#E2E2E2",
                    movable: false,
                    click: function (e, node) {
                        if (node instanceof go.Node) {
                            highlightLinks(node);
                        }
                    }
                }, new go.Binding("isShadowed", "root"),
                new go.Binding("movable", "root"),
                $(go.Shape, "RoundedRectangle", {
                        strokeWidth: 0,
                        stroke: "#E2E2E2",
                        fill: "white",
                        stretch: go.GraphObject.Horizontal
                    },
                    new go.Binding("strokeWidth", "root", (v) => {
                        return v ? 1 : 0
                    })
                ),
                $(go.Panel, go.Panel.Vertical,
                    $(go.Panel, go.Panel.Horizontal, {
                            alignment: go.Spot.Left,
                            defaultStretch: go.GraphObject.Horizontal,
                            portId: ""
                        },
                        new go.Binding("alignment", "root", (v) => {
                            return v ? go.Spot.Center : go.Spot.Left
                        }),
                        $(go.Picture, {margin: 3, width: 16, height: 16}, new go.Binding("source", "source")),
                        $(go.TextBlock, {margin: 3, editable: false}, new go.Binding('text').makeTwoWay()),
                        $("SubGraphExpanderButton", {
                            margin: 3,
                            visible: false
                        }, new go.Binding("visible", "loadMore", (v) => {
                            return !v
                        })),
                        $("Button", {
                                visible: true, margin: 3, click: function (e, obj) {
                                    diagram.select(obj.part)
                                }
                            },
                            $(go.Shape, "PlusLine", {
                                stroke: '#424242',
                                strokeWidth: 2,
                                desiredSize: new go.Size(8, 8)
                            }), new go.Binding("visible", "loadMore")
                        ),
                        $("Button", {
                                visible: true, margin: 3, click: function (e, obj) {
                                    if (obj.part) {
                                        selfComponent.props.onClickMoreButton(obj.part.data.key);
                                    }
                                }
                            },
                            $(go.Picture, {
                                    source: "/img/random.svg",
                                    desiredSize: new go.Size(13, 13)
                                }, new go.Binding("source", "fullLineageDisplayed", (v) => {
                                    return v ? "/img/randomHighlight.svg" : "/img/random.svg"
                                }),
                                {toolTip: $("ToolTip", $(go.TextBlock, {margin: 4}, "Get all links"))})
                        )
                    ),
                    $(go.Shape, "LineH", {
                            visible: true,
                            stroke: TECHNOLOGY_COLOR,
                            height: 10,
                            stretch: go.GraphObject.Horizontal
                        },
                        new go.Binding("visible", "root")),
                    $(go.Placeholder,{padding:new go.Margin(0,0,0,10)})
                )
            )

Can you have a look at my code and tell me if I’m doing something wrong… I don’t understand how to improve the template. Maybe I can change with a table or something else.

Thank you !

Since you seem to be having a problem with the routing of links, what is your link template(s)?

Are there links not shown between the nodes within each group that organizes the order and indentation of those nodes? If so, I suggest that you use a TreeLayout such as in GoJS TextBlocks -- Northwoods Software Furthermore you then have to make sure that the links shown in your screenshot that connect nodes within the same Group do not act to affect that TreeLayout. You can do that by setting or binding Link.isLayoutPositioned to false on only those links. Example: Org Chart Extras

Alternatively

You could implement your own indentation on each node, instead of having a Group.layout being a TreeLayout. This might be easier for you since you have a fixed number of indentation levels and can know ahead of time how deep you want to indent each node. But this does not seem to be your design, because you are using a TreeLayout as the Group.layout and do not have a simple way of controlling some variable space on the left side of each Node.

I’m not sure about the indentation of the name of a director, but until you have decided exactly how to do that, there’s no point in worrying about it.

Thank you for your reply.

Below the link template code :

//Define link template here
        diagram.linkTemplate =
            $(go.Link, {
                    selectable: false,
                    isHighlighted: false,
                    routing: go.Link.AvoidsNodes,
                    corner: 10,
                    adjusting: go.Link.None,
                    curve: go.Link.JumpGap,
                    toEndSegmentLength: 20,
                    fromEndSegmentLength: 50
                },
                $(go.Shape, {strokeWidth: 2, stroke: "#757575"}, new go.Binding("stroke", "isHighlighted", (h) => {
                    return h ? "#2196f3" : "#757575"
                }).ofObject()),
                $(go.Shape, {
                    toArrow: 'Standard',
                    strokeWidth: 1,
                    stroke: "#757575",
                    fill: "#757575"
                }, new go.Binding("stroke", "isHighlighted", (h) => {
                    return h ? "#2196f3" : "#757575"
                }).ofObject(), new go.Binding("fill", "isHighlighted", (h) => {
                    return h ? "#2196f3" : "#757575"
                }).ofObject()),
                $(go.Shape, {
                    fromArrow: "Circle",
                    stroke: "#757575",
                    strokeWidth: 0.5,
                    fill: "#757575"
                }, new go.Binding("stroke", "isHighlighted", (h) => {
                    return h ? "#2196f3" : "#757575"
                }).ofObject(), new go.Binding("fill", "isHighlighted", (h) => {
                    return h ? "#2196f3" : "#757575"
                }).ofObject())
            );

I don’t think so.

In my conception I suppose that I don’t know the tree level. As you can see on my implementation screenshot, I have 2 buttons that can add subgroup or/and subnodes dynamically by calling my back-end.

I will try this property.

Thank you for your precious help.

I just try the property and nothing seems to change…

Which design choice have you made? Are you using links to define the relationships between nodes in each group, so that a TreeLayout can arrange all of the nodes in a vertical ordering with appropriate indentation? Or are you not using links and TreeLayout to arrange the tree-view within each group?

I thought that you are using nested groups to achieve the organization that you want. So setting or binding isLayoutPositioned is not desired.

What changes did you make that had no effect?

I am now thinking that you should not be using a TreeLayout as your Group.layout. Try using a GridLayout with GridLayout.wrappingColumn set to 1.

Also, you need to set the fromSpot and toSpot on each Node.

It is good that you have set Group.avoidable to false, since you expect links to cross over groups.

I suggest that you set Shape.strokeWidth to zero on your arrowheads. That way you also do not need to set their Shape.stroke, because their stroke will not be drawn when the stroke width is zero.

Hello Walter,

I’m not using links in a TreeLayout to define relationships between nodes. It’s only based on group template and the padding property (nested groups).
If I would like to do it with links, is it possible to hide them ? (I don’t want to display the link between a parent group to the child group/node)

I will try this layout instead of the TreeLayout.

Actually I only set this property to Group template and not on Node template. I will add this property.

Not sure to understand this point. Do I need to add a binding function to draw them if necessary ?

Thank you very much !

The appearance of the arrowheads is a very minor point. Never mind.

Are there any links going to or from Groups? Or do they all go to and from simple Nodes? In other words, will there be links going to or from “imdb” or “Allocine”?

I am also guessing that the “strange padding” of the “nom” node is due to the node being aligned to the center of the available area. Try setting { alignment: go.Spot.TopLeft } on the Placeholder.

No there are no link possible between what I call the “root” node which is in go js, the first original group with root property (used for binding).

Well done for this tip, It resolve the “strange padding”. Thank you !

Hello Walter,

I change the Group template to GridLayout the diagram look goods. I also add the fromSpot and toSport property to my go.Panel in the group template and the link is now well displayed.

Thank you for your help.