nodeKeyProperty for nested model data

We can use the nodeKeyProperty, linkKeyProperty, linkFromPortIdProperty etc. to specify what model data will be used as keys/ids for the corresponding parts. It is also possible to use nested properties here?

Example model:

{
  data: {
    id: 2
  }
}

Is it possible to use the 2 here as a key?

Assuming you don’t need to modify any node data key:

    myDiagram.model = $(go.GraphLinksModel, {
      nodeKeyProperty: d => d.data.id,
      nodeDataArray:
        [
          { data: { id: 1 }, text: "Alpha", color: "lightblue" },
          { data: { id: 2 }, text: "Beta", color: "orange" },
          { data: { id: 3 }, text: "Gamma", color: "lightgreen" },
          { data: { id: 4 }, text: "Delta", color: "pink" }
        ],
      linkDataArray:
        [
          { from: 1, to: 2 },
          { from: 1, to: 3 },
          { from: 2, to: 2 },
          { from: 3, to: 4 },
          { from: 4, to: 1 }
        ]
    });

Also, if you are going to ask about binding to subproperties other than predefined ones such as the node key or the link from or link to key properties:

I had found this in an earlier discussion, thanks :)

As a follow up of this question; I’m now creating new links using the LinkingTool and I would like the linking tool to update the nested key properties, example:

Tool configuration:

this.toolManager.linkingTool.archetypeLinkData = {
      start: {
        entityId: "",
        waypointName: "",
      },
      end: {
        entityId: "",
        waypointName: "",
      },
    };

model configuration:

this.linkFromKeyProperty = (ld: any) => ld.start.entityId;
this.linkFromPortIdProperty = (ld: any) => ld.start.waypointName;
this.linkToKeyProperty = (ld: any) => ld.end.entityId;
this.linkToPortIdProperty = (ld: any) => ld.end.waypointName;

According to the docs of the archetypeLinkData, I would expect the data would be updated with use of the properties that are set. However; I did only specify a getter, how can I specify the setter?

Thanks!

<!DOCTYPE html>
<html>
<head>
  <title>Minimal GoJS Sample</title>
  <!-- Copyright 1998-2021 by Northwoods Software Corporation. -->
  <script src="go.js"></script>
  <script id="code">
  function init() {
    var $ = go.GraphObject.make;

    myDiagram =
      $(go.Diagram, "myDiagramDiv",
        {
          "linkingTool.archetypeLinkData": { start: {}, end: {} },
          "undoManager.isEnabled": true,
          "ModelChanged": function(e) {     // just for demonstration purposes,
            if (e.isTransactionFinished) {  // show the model data in the page's TextArea
              document.getElementById("mySavedModel").textContent = e.model.toJson();
            }
          }
        });

    myDiagram.nodeTemplate =
      $(go.Node, "Spot",
        $(go.Panel, "Auto",
          $(go.Shape,
            { fill: "white" },
            new go.Binding("fill", "color")),
          $(go.TextBlock,
            { margin: 8, editable: true },
            new go.Binding("text").makeTwoWay())
        ),
        $(go.Shape, "Circle",
          { width: 7, height: 7, alignment: go.Spot.Left,
            portId: "I", toLinkable: true, toSpot: go.Spot.Left }),
        $(go.Shape, "Circle",
          { width: 7, height: 7, alignment: go.Spot.Right,
            portId: "O", fromLinkable: true, fromSpot: go.Spot.Right, cursor: "pointer" })
      );

    myDiagram.linkTemplate =
      $(go.Link,
        { relinkableFrom: true, relinkableTo: true, reshapable: true, resegmentable: true },
        $(go.Shape),
        $(go.Shape, { toArrow: "OpenTriangle" })
      );

    myDiagram.model = $(go.GraphLinksModel, {
      copyNodeDataFunction: function(d, m) {
        return { data: {}, text: d.text, color: d.color };
      },
      nodeKeyProperty: function(d, val) {
        if (arguments.length > 1) d.data.id = val; else return d.data.id;
      },
      copyLinkDataFunction: function(ld, m) {
        return { start: { waypointName: ld.start.waypointName }, end: { waypointName: ld.end.waypointName } };
      },
      linkFromKeyProperty: function(ld, val) {
        if (arguments.length > 1) ld.start.entityId = val; else return ld.start.entityId;
      },
      linkFromPortIdProperty: function(ld, val) {
        if (arguments.length > 1) ld.start.waypointName = val; else return ld.start.waypointName;
      },
      linkToKeyProperty: function(ld, val) {
        if (arguments.length > 1) ld.end.entityId = val; else return ld.end.entityId;
      },
      linkToPortIdProperty: function(ld, val) {
        if (arguments.length > 1) ld.end.waypointName = val; else return ld.end.waypointName;
      },
      nodeDataArray:
        [
          { data: { id: 1 }, text: "Alpha", color: "lightblue" },
          { data: { id: 2 }, text: "Beta", color: "orange" },
          { data: { id: 3 }, text: "Gamma", color: "lightgreen" },
          { data: { id: 4 }, text: "Delta", color: "pink" }
        ],
      linkDataArray:
        [
          { start: { entityId: 1, waypointName: "O" }, end: { entityId: 2, waypointName: "I" } },
        ]
    });
  }
  </script>
</head>
<body onload="init()">
  <div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:600px"></div>
  <textarea id="mySavedModel" style="width:100%;height:250px"></textarea>
</body>
</html>

Remember that JSON formatted text cannot support serializing functions, so Model.toJson cannot save those …Property functions. Whenever you construct a model you will need to re-set those properties before using the model.