Moveable/Draggable ports

i’m implementing a telecom solution where ports are significant. I have a use case where a user has link between two ports and he would like to drag the ports to some other place on the node in order to make room for other ports or in order to switch node places.
Is it possible to let the user drag a port and position it on other place on the node ?

Sure, see the following samples:

Will try.

if i use the draggable ports algorithm where nodes server as ports inside a group node, if i use the LayeredDigraphLayout auto layout algorithm, will the layout algorithm work on the “ports” nodes as well, or only on the group nodes ?
In other words, will the “port” nodes participate in the layout algorithm and layed step by step link after link, right to left ?

OK, so we’re just talking about the Draggable Ports sample, where each port is actually implemented as a separate Node.

Whether the Group.layout affects the port Nodes is up to the choices you make. If you set Part.isLayoutPositioned to false on those port Nodes, they won’t be moved by the Layout. However, I suspect you do want to move those port nodes, because the size of the Group will probably change and you probably want to keep those port Nodes on the edges of the Group. So really, you do want to have the layout position those port Nodes in the Group after all. You just need to override the Layout.commitNodes method in order to position those port Nodes where you want them to be.

I see that i can use the portshifting example and the PortShiftingTool.js, to implement a moveable port.
On the other hand if i use the itmearray (from the dynamic ports example) model to hold ports, i wonder how should i model the itemarray in the node templat ? as a spot array ? (The PortShiftingTool requires that all ports will be located as Spot on the node).

Yes, that’s right – you would bind Panel.itemArray on a “Spot” Panel, where the first (and main) element is the Shape or nested Panel body to which the item elements are positioned by GraphObject.alignment and GraphObject.alignmentFocus. Presumably at least the “alignment” will be data bound to some property on the item data in order to control its spot relative to the panel’s main element.

I guess the Pipes sample demonstrates using a “Spot” Panel that has a variable number of ports: Pipes.

I’m not sure i know how to create such template. On one hand i have to create a Panel with itemTemplate for the ports itemArray and on the other hand i need to add a Shape with portId into the Panel template.
So you have a sample ?

To which object should i bind the portId ? to the Panel (like in pipe sample) or to the Shape (like in PorShiftt sample)

By the way, the ports in the pipe sample are declared in an itemArray, and they are not movable.
I face the same problem. Once i declare them in an itemArray, i cannot move them.
Only when they are directly declared in a Shape, the port is movable.

I am assuming that you are using extensions/PortShiftingTool, as you indicated above.

Is the Panel with the Binding of “itemArray” also a “Spot” Panel?

Do you have a Binding of “portId” on the Panel.itemTemplate, and do you make sure each portId has a unique value within the node?

These requirements are determined by the PortShiftingTool.findPort method.

The itemArray are binded on the main Panel, the Node.
Yes, i bind the portId on the Panel.itemtemplate and the portId is unique : “-9”.
i noticed that the canStart() method on PortShiftingTool.js returns false for the
question :
if (!this.isBeyondDragSize()) and as result, does not continue to this.findPort()

here is my node template definition.

Until the user actually moves the mouse/finger enough, the PortShiftingTool won’t run.

It’s suspicious that you set the Shape.alignment, when you really want to set the Panel.alignment – that is you want to set the alignment of the port, not of the Shape within the Panel.

when i use the following template, it works fine (the portId is hard coded into the shape) :
diagram.nodeTemplate =
$(go.Node, “Spot”, nodeStyle(),
$(go.Picture, { width: 100, height: 100 }, new go.Binding(“source”) ),
$(go.Shape, “Rectangle”, portStyle(false), { portId: “dd” })

But when i use the following template with itemArray, i cannot move the port:

diagram.nodeTemplate =
$(go.Node, “Spot”, nodeStyle(),
new go.Binding(“itemArray”, “items”),
$(go.Picture, { width: 100, height: 100 }, new go.Binding(“source”) ),

            itemTemplate : $(go.Panel,
                new go.Binding("portId", "id"),
                new go.Binding("alignment", "spot", go.Spot.parse),
                $(go.Shape, "Rectangle", portStyle(false))

var nodeDataArray = [
{ key: nodeKey++, source: “icons/Router.svg”, siteId: “100/TS-XDM1000-KSDH-A-01”, loc: “0 500”, items: [ { id:“H01Rx”, spot: “0.5 0.5 0.5 0.5” } ] },

Where do i go wrong ?

You probably have done nothing wrong – there’s a bug in PortShiftingTool.findPort, since it does not handle nested panels correctly. You can modify extensions/PortShiftingTool.js as follows:

PortShiftingTool.prototype.findPort = function() {
  var diagram = this.diagram;
  var e = diagram.firstInput;
  var elt = diagram.findObjectAt(e.documentPoint, null, null);

  if (elt === null || !(elt.part instanceof go.Node)) return null;
  while (elt !== null && elt.panel !== null) {
    if (elt.panel.type === go.Panel.Spot && elt.panel.findMainElement() !== elt &&
        elt.portId !== null && elt.portId !== "") return elt;
    elt = elt.panel;
  return null;

I applied the changes and it works perfect.

The bug fix does support nested panels, yet, when i move the ports and then move the whole node, the ports return to their original location.
If i use the simple template without nested panels, the port remain at its last location.

I played around with my node template and commented out the alignment binding inside the itemTemplate.
Once i commentated out, the ports retain at their last location.
So, I cannot use the alignment binding ?
See the node Template :
diagram.nodeTemplate =
$(go.Node, “Spot”, nodeStyle(),
$(go.Picture, { width: 100, height: 100 }, new go.Binding(“source”) ),
new go.Binding(“itemArray”, “items”),
itemTemplate : $(go.Panel, “Vertical”,
new go.Binding(“portId”, “id”),
// new go.Binding(“alignment”, “spot”, go.Spot.parse),
$(go.Shape, “Rectangle”, portStyle(false)),
$(go.TextBlock, { margin: 4 }, new go.Binding(“text”, “id”))