How to create a Link without using Models

All of the examples I have found use GraphLinksModel or TreeModel to create links.

How does one create a link using JS?

For example:

  diagram.add(
    $(go.Link,
      {
        fromNode: obj1.part,
        toNode: obj2.part,
      },
      $(go.Shape),
      $(go.Shape, { toArrow: "Standard" })),
  );

When I use the snippet above, the link is drawn in the wrong place, and it’s always drawn in the same place regardless of the fromNode and toNode.

What are the value of obj1 and obj2? I just tried your code in an existing app, making sure the values of fromNode and toNode were existing Nodes in the Diagram. It worked as I think you were expecting it to.

But doing so is creating an unbound Link. That means Link.data is null and data binding does not work within that Link.

It is much more commonplace to add the link to the model.

If you are using a GraphLinksModel, call GraphLinksModel.addLinkData. The State Chart sample, State Chart, demonstrates this in the addNodeAndLink function.

If you are using a TreeModel, call TreeModel.setParentKeyForNodeData.

The problem with using GraphLinksModel is that you have define a node template and a group template for each kind of node or group that you want to display. So if there is a node display that is only used once, it has to be a template. This makes it awkward to use with a diagram that is heterogeneous.

Here’s a more complete example:

  var obj1, obj2;

  function makeNode(name, alignment) {
    return $(go.Panel, "Auto",
      { alignment: alignment, width: 120, height: 120 },
      $(go.Shape, "Pentagon",
        {
          fill: "white",
        }),
      $(go.TextBlock,
        { text: name, font: "10pt Sans-Serif", margin: 4, wrap: go.TextBlock.WrapFit, textAlign: "center" }),
    );
  }
  
  diagram.add(
    $(go.Node, "Spot",
      $(go.Shape, "Rectangle",
        { fill: "transparent", stroke: null, width: 400, height: 200 }),
      (obj1 = makeNode("obj1", new go.Spot(0, 0.5, 60, 0))),
      (obj2 = makeNode("obj2", go.Spot.Center)),
    ));

  diagram.add(
    $(go.Link,
      {
        fromNode: obj1.part,
        toNode: obj2.part,
      },
      $(go.Shape),
      $(go.Shape, { toArrow: "Standard" })),
  );

Your makeNode function does not make a Node – it merely makes a Panel consisting of a Shape surrounding a TextBlock.

The Node that you build and add programmatically to the Diagram holds two panels results from two calls to your makeNode function. That’s fine, but I hope you realize that there is only one Node in your Diagram. So when you add the Link, it connects that one Node with itself.

That one node is about 400 wide and 200 high, and it’s transparent with no stroke, so the result is:

I have selected the node so that its bounds are clear.

Regarding using templates, have you read these pages?
https://gojs.net/latest/intro/usingModels.html
https://gojs.net/latest/intro/dataBinding.html

If I change makeNode to return $(go.Node, ... then the error is “Cannot add a Part to a Panel: Node#482; use a Panel instead”

I remember trying this before.

Yes, I actually wrote the thing using models and binding first, and then switched to this approach because the template-based approach just didn’t seem to be working out for my use case.

OK, so what is your use case that either simple Bindings don’t work or multiple templates won’t work?

Is it the case that you can’t use Spot layouts with Nodes? Only with simpler things like GraphObjects?

There is a Panel layout named “Spot”. That arranges GraphObjects within a Panel: GoJS Panels -- Northwoods Software.

But you cannot put a Part inside a Panel, so you cannot have Nodes within the visual tree of another Node. What would normally be done there is to have Nodes be members of a Group. GoJS Groups -- Northwoods Software

The type of diagram I am trying to create looks like something that you might create with Google Drawings or Visio; it’s not really a data-driven problem where there are a bunch of nodes that need to be arranged in a grid or a tree. The layout that’s required doesn’t have that kind of regular structure. The way that containers (panels) are used to contain various nodes, connected by links, some sections expandable, seems to be a pretty good fit for GoJS, but not really a good fit for the data-model approach.

I was trying to use the Spot layout to organize the objects on the canvas without having to specify exact X-Y coordinates for each one. But it seems like Spot layout is only capable of laying out more simple GraphObjects, and not Nodes.

So to create the diagram that I am working on, it seems that I will have to pretty much specify the X-Y coordinates of each thing.

There are some sub-graphs within the overall diagram for which an automated layout will come in handy (e.g. a bunch of identical nodes representing servers or containers which should be arranged in a grid) but most of the diagram, as I said, does not have that kind of simple structure that lends itself to the data-driven approach.

Changing makeNode to return a Node and placing them within a Group still results in the same error : “Cannot add a Part to a Panel”

  function makeNode(name, alignment) {
    return $(go.Node, "Auto",
      { alignment: alignment, width: 120, height: 120 },
      $(go.Shape, "Pentagon",
        {
          fill: "white",
        }),
      $(go.TextBlock,
        { text: name, font: "10pt Sans-Serif", margin: 4, wrap: go.TextBlock.WrapFit, textAlign: "center" }),
    );
  }
  
  diagram.add(
    $(go.Group, "Spot",
      $(go.Shape, "Rectangle",
        { fill: "transparent", stroke: null, width: 400, height: 200 }),
      (obj1 = makeNode("obj1", new go.Spot(0, 0.5, 60, 0))),
      (obj2 = makeNode("obj2", go.Spot.Center)),
    ));

That error is correct – as I said, you cannot add a Part to the visual tree of a Panel.

It still seems to me that your app would benefit from using a model. Have you seen these samples?

I’m not familiar with Google Drawings, but I know that many customers have used our products to replace their dependence on Visio.

In hindsight, what I was trying to do was use Spot-style layout on Nodes; which is not supported.

The distinction between Nodes and plain GraphObjects is not super-clear to new users. It’s still not entirely obvious to me why Spot positioning can’t be used with all kinds of objects. But I do now at least understand the difference.

I’ve gone the route of making lots of templates for my Nodes, Groups and Links and then using the model-based approach.

Thanks for your prompt assistance!

You can use GridLayout to implement something like Vertical or Horizontal Panels.

You can use TableLayout, https://gojs.net/latest/extensions/TableLayout.js and Table Layout, whose implementation was taken from the implementation of Table Panel.