Permanent adornment with indicators

Hi Everyone,

I am struggeling with creating some kind of permanent node adornment which places some node connection inidcators outside of the actual node body:

My problem is that when I make these indicators part of the actual node template, then the selection border gets drawn around them:

And if I make them part of the node adornment they are only visible when the node is selected. However, the indicators should be visible at all time but visually be outside of the node body.

I would highly appreciate any ideas on how to achieve that! :)

Yes, if you want something to be shown all the time, you need to include it in the template.

But you can control which element gets the selection adornment. Try giving what you want a name and then setting Part.selectionObjectName to that name. If you search the samples you will find a number of examples of this.
GoJS Selection -- Northwoods Software

An alternative is not to use selection adornments, but to modify the node itself.
GoJS Selection -- Northwoods Software

Brilliant. Thank you for the quick response. We’re almost there:

Now there are two problems:

  1. The links are not routed to the box but to the invisible border to the node. This is probably the biggest issue because it everything that I place outside of the box will affect the link routing, correct?
  2. The selection adornment tools also float in the wrong place. That could probably be fixed by your alternative proposal.

Each of your nodes only has a single port, yes? In other words, you have not set or bound portId to a string on any of the elements of your node?

If so, then the whole Node is acting as the default port for the node. (Hence Node.port == Node.) You can make that rounded rectangular shape be the port by setting its GraphObject.portId to the empty string, which is the default port identifier.

    $(go.Shape, . . .,
      { . . .,
        portId: ""
      })

Or choose some other element such as a Panel to make the default port by setting its port identifier to be the empty string.

Alright. That worked:

Leaves only the issue with the floating adornment buttons to be misplaced because they adorn the whole node, not just the body shape. Is there anything similar to help here? Can I specify the object to be adorned by the shape name?

This is how I create the adornment:

if (node.data.isSelected) {
  const adornment = this.createNodeAdornment(diagram);
  adornment.adornedObject = node;

  node.addAdornment("nodeTools", adornment);
  ...

OK. I fixed it by setting a negative margin on Placeholder in the Adornment template:

.add(new go.Placeholder(
        {
          background: null,
          padding: new go.Margin(-20, 3, 3, 3),
          isActionable: true
        }
      ))

This probably not the most elegant solution but it works for me. Thank you, Walter for your help!

If you had set the Adornment.adornedObject to be just that body shape or panel, instead of the whole Node, then the Placeholder would take the size and position just of that adornedObject.

Dear walter, I was thinking about that too. However, I simply did not find a method for retrieving template shapes / children from a given go.Node…

Adornments are data bound to the same data object that the adorned Part is bound to – the Part.data. So if the Shape.figure is data bound in the template, you can use the same binding in the Adornment.

And if the template’s Shape is constant (no Binding), well then your Adornment can have a constant Shape too.

I’m sorry, Walter but I cannot quite follow you.

From what I understand is that if I set the Adornment.adornedObject to the rounded rectangle shape of my node, then the adornment will take the size and position of that particular object. Perfect. So what I am looking for is something like this:

adornment.adornedObject = node.getTemplatePartByName('RoundedRectangle')

I do not quite understand what the data binding has to do with Shape figures…

Oh, I misunderstood you – I thought you wanted to get some properties of the adorned part or object.

Just give what you want a name in the template, and then you can call Panel.findObject to find that object in any Node copied from the template.

Perfect. Thanks! :-)