Link the dynamic spots that comes on mouse click by giving them unique portId

Here is my code :

function init() {
var $ = go.GraphObject.make;

myDiagram =
  $(go.Diagram, "myDiagramDiv",
    { initialContentAlignment: go.Spot.Center, "undoManager.isEnabled": true });

myDiagram.nodeTemplate =
  $(go.Node, "Spot",
    { selectionObjectName: "BODY" },
    new go.Binding("itemArray", "spots"),
    { // each spot is represented by a Panel holding a circular Shape
      // at a particular alignment relative to the "BODY"
      itemTemplate:
        $(go.Panel,
          $(go.Shape, "Circle",
            {
              fill: "red",
              portId: " ",
              toLinkable: true,
              fromLinkable: true,
              strokeWidth: 0, 
              width: 8, height: 8
            }),
          new go.Binding("alignment", "spot", go.Spot.parse).makeTwoWay(go.Spot.stringify)
        ),
      // when the user clicks on the node, add a "spot"
      click: function(e, obj) {
        e.diagram.startTransaction();
        // convert click point into Spot in node's bounds
        var pt = e.documentPoint;  // in document coordinates
        var node = obj.part;
        var b = node.actualBounds;  // will be in document coordinates
        var spot = new go.Spot(Math.max(0, Math.min((pt.x - b.x) / b.width, 1)),
                               Math.max(0, Math.min((pt.y - b.y) / b.height, 1)));
        // add an Object describing the spot's location (as a Spot value)
        var spotsArray = node.data.spots;
        if (!Array.isArray(spotsArray)) spotsArray = node.data.spots = [];
        e.diagram.model.addArrayItem(spotsArray, { spot: go.Spot.stringify(spot) });
        e.diagram.commitTransaction("added spot");
      }
    },
    $(go.Panel, "Auto",
      { name: "BODY", width: 100, height: 100 },
      $(go.Shape, { fill: "whitesmoke" }),
      $(go.TextBlock, { editable: true },
        new go.Binding("text").makeTwoWay())
    )
  );

myDiagram.model = $(go.GraphLinksModel,
  {
    copiesArrays: true,  // make sure the data.spots Array is copied
    copiesArrayObjects: true,  // make sure the Objects in those Arrays are also copied
    model.linkFromPortIdProperty = "fromPort";
    model.linkToPortIdProperty = "toPort";
    nodeDataArray: [ ],
    linkDataArray: [ ]
  });

}

what i need is whenever a spot is made on the mouse click it should have unique portId and in these properties:
model.linkFromPortIdProperty = “fromPort”;
model.linkToPortIdProperty = “toPort”;
when i link the any two spots fromPort should have the one spots portId and toPort should have the other spots portId.
Because of portId: " " i am able to connect only first two spots after that all the links connect on the same spots (see in the screenshot).

I would not bother setting portId: " " in the itemTemplate, but make sure there is a Binding for “portId” to some property on the item that names the port.

Then I would make sure that property exists on the object, with a unique name within that node, when you call addArrayItem.

Your model initialization is syntactically illegal.

Can you please add sample code example to achieve this? I read the gojs documentation for port spot etc but not getting clearly.

Delete this line portId: " ", in the itemTemplate, and add new go.Binding("portId"), on that Shape.

Then just before calling Model.addItemArray, compute a unique port id and pass it as the value of the portId property on the new Object that you pass to addItemArray. You can iterate over the node.ports to see what GraphObject.portIds the node already has.

Thanks
I removed the portId and adding the binding for portId. It is clear to me. I am adding the spot and then trying to add portid on it. to behave like ports. My goal is the create custom link-able spots.
itemTemplate:
GO(go.Panel,
{toLinkable: true,fromLinkable:true},
new go.Binding(“portId”, “portId”),
GO(go.Shape, “Circle”,
{
fill: “red”,
strokeWidth: 0, width: 8, height: 8
}

),
new go.Binding(“alignment”, “spot”, go.Spot.parse).makeTwoWay(go.Spot.stringify)
),

// At the place of adding custom spot i did this. But it does not reflect anything

click: function(e, obj) {
e.diagram.startTransaction();
// convert click point into Spot in node’s bounds
var pt = e.documentPoint; // in document coordinates
var node = obj.part;
var b = node.actualBounds; // will be in document coordinates
var spot = new go.Spot(Math.max(0, Math.min((pt.x - b.x) / b.width, 1)),
Math.max(0, Math.min((pt.y - b.y) / b.height, 1)));
// add an Object describing the spot’s location (as a Spot value)
// this is unique name for port id
var name =“spot_”+random_string();

          spot.portId = name;
          var spotsArray = node.data.spots;
          if (!Array.isArray(spotsArray)) spotsArray = node.data.spots = [];
          e.diagram.model.addArrayItem(spotsArray, { spot: go.Spot.stringify(spot) });
          e.diagram.commitTransaction("added spot");
        }  
      }

The Spot class has no property named “portId”, so your assignment has no effect. http://gojs.net/latest/api/symbols/Spot.html

You have to add “portId” as a property on the new item Object that you call addArrayItem with.

You want to say that I have to do it like this
var newspotdata = {
portId: name
};
and then insert newspotdata object in to addArrayItem :
e.diagram.model.addArrayItem(spotsArray, { spot: go.Spot.stringify(spot) }, newspotdata);

That’s not a valid call – http://gojs.net/latest/api/symbols/Model.html#addArrayItem.

I am adding spot in the addArrayItem
e.diagram.model.addArrayItem(spotsArray, { spot: go.Spot.stringify(spot) });
So i am not understanding how to add portid in the panel of that particular spot.

Add , portId: name to that new Object.

There is probably a sample or two that does something like this.

Thank you so much,Now links are working for the custom spots.

I have one more query that is when i rotate the shapes the spot added on them changes their position. Also these spots come outside the shape if i resize the shape and then rotate it.If i only resize the shape it is working fine but rotation of the shape is causing this issue.So is it possible that the spots do not change their position when i rotate the shape?
My code is the same as i posted in my first post above.

When I add these declarations on the Node:

        {
          selectionObjectName: "BODY",
          locationSpot: go.Spot.Center,
          rotatable: true,
          resizable: true, resizeObjectName: "BODY"
        },

Everything seems to rotate and resize well for me.

Thank you,
Spots position is fixed now.But when i rotate a shape, on-click spots are not coming on the exact position of mouse click .There is so much variation between mouse click position and the position where spot comes after click.

Yes, the code I gave you did not anticipate rotation. You’ll need to rotate the click point back to the unrotated coordinates before computing the spot of the new port.

So,How can I achieve this? or can I make these spots drag-able also.So that I can move the spot to required position when it comes somewhere else after rotation of the shape.