Binding ofModel with empty source property copy

Hi,

We are using an ofModel() binding with an empty '' sourceprop. This works as expected:

The returned value is the data stored in model.modelData. However, when the clipboard is involved: selecting a node an hitting ctrl+c, the value becomes the nodeData, is this expected? Or shouldn’t we be using ofModel() in combination with an empty sourceprop?

Example:

<!DOCTYPE html>
<html>

<head>
  <title>Model data empty src prop</title>
  <meta charset="UTF-8">
</head>

<body onload="init()">
  <div id="app">
    <div id="diagram" style="border: solid 1px black; width:100%; height:300px"></div>
  </div>

  <script src="https://unpkg.com/gojs"></script>
  <script id="code">
    function init() {
      var $ = go.GraphObject.make;  // for conciseness in defining templates

      myDiagram = $(go.Diagram, "diagram", {
        "undoManager.isEnabled": true
      });

      // define a simple Node template
      myDiagram.nodeTemplate = $(go.Node, "Auto",
        $(go.Shape, "RoundedRectangle", { strokeWidth: 0 },
          new go.Binding("fill", "", (value, target) => {
            return value[target.diagram.model.modelData.colorVisualizationSetting]
          }),
          new go.Binding("fill", "colorVisualizationSetting", (value, target) => {
            return target.part.data[value]
          }).ofModel(),
        ),
        $(go.TextBlock,
          { margin: 8, font: "bold 14px sans-serif", stroke: '#333' },
          new go.Binding("text", "", (val) => {
            console.log(val)
            return val.prop1
          }).ofModel()
        )
      );

      myDiagram.model = new go.GraphLinksModel(
        [
          { key: "alpha", color1: 'red', color2: 'blue', color3: 'green' },
          { key: "bravo", color1: 'green', color2: 'red', color3: 'blue' },
          { key: "charlie", color1: 'blue', color2: 'green', color3: 'red' },
        ]
      );
      myDiagram.model.modelData = {
        prop1: 'prop1',
        prop2: 'prop2',
      }
      myDiagram.model.linkKeyProperty = 'key'
    }
  </script>
</body>

</html>

See console log

If you are going to expect nodes to be created from a template that depends on Model.modelData, you need to be sure that is initialized first. Although what you are doing, setting Model.modelData after setting Diagram.model, can work, you have to write your bindings more carefully to avoid problems when properties are missing because they haven’t been set yet.

Also, your code is depending on the colorVisualizationSetting property, which is missing from your shared data object.

function init() {
  var $ = go.GraphObject.make;  // for conciseness in defining templates

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

  // define a simple Node template
  myDiagram.nodeTemplate = $(go.Node, "Auto",
    $(go.Shape, "RoundedRectangle", { strokeWidth: 0 },
      new go.Binding("fill", "", (value, target) => {
        return value[target.diagram.model.modelData.colorVisualizationSetting]
      }),
      new go.Binding("fill", "colorVisualizationSetting", (value, target) => {
        return target.part.data[value]
      }).ofModel(),
    ),
    $(go.TextBlock,
      { margin: 8, font: "bold 14px sans-serif", stroke: '#333' },
      new go.Binding("text", "", (val, tb) => {
        console.log(val, tb)
        return val.prop1;
      }).ofModel()
    )
  );

  myDiagram.model = $(go.GraphLinksModel,
    {
      linkKeyProperty: 'key',
      modelData: {
        colorVisualizationSetting: "color1",
        prop1: 'prop1',
        prop2: 'prop2',
      },
      nodeDataArray:
      [
        { key: "alpha", color1: 'red', color2: 'blue', color3: 'green' },
        { key: "bravo", color1: 'green', color2: 'red', color3: 'blue' },
        { key: "charlie", color1: 'blue', color2: 'green', color3: 'red' },
      ]
    });
}

Sorry for the cluttered example. I tried your example and it gives me the same result; when you press ctrl+c, the val that gets logged is the node data and not the modelData while the binding should be ofModel().

My less cluttered example to illustrate:

<!DOCTYPE html>
<html>

<head>
  <title>Model data empty src prop</title>
  <meta charset="UTF-8">
</head>

<body onload="init()">
  <div id="app">
    <div id="diagram" style="border: solid 1px black; width:100%; height:300px"></div>
  </div>

  <script src="https://unpkg.com/gojs"></script>
  <script id="code">
    function init() {
      var $ = go.GraphObject.make;  // for conciseness in defining templates

      myDiagram = $(go.Diagram, "diagram", {
        "undoManager.isEnabled": true
      });

      // define a simple Node template
      myDiagram.nodeTemplate = $(go.Node, "Auto",
        $(go.Shape, "RoundedRectangle", { strokeWidth: 1, fill: null }),
        $(go.TextBlock,
          { margin: 8, font: "bold 14px sans-serif", stroke: '#333' },
          new go.Binding("text", "", (val) => {
            console.log(val)
            return val.prop1
          }).ofModel()
        )
      );

      const model = new go.GraphLinksModel(
        [
          { key: "alpha", color1: 'red', color2: 'blue', color3: 'green' },
          { key: "bravo", color1: 'green', color2: 'red', color3: 'blue' },
          { key: "charlie", color1: 'blue', color2: 'green', color3: 'red' },
        ]
      );
      model.modelData = {
        prop1: 'prop1',
        prop2: 'prop2',
      }
      model.linkKeyProperty = 'key'

      myDiagram.model = model;
    }
  </script>
</body>

</html>

Sorry, I forgot to address that issue after updating the code so that it could run.

The copied data is not in a model, so there is no shared model data object to pass as the binding source value. Maybe for v2.2 we should have the clipboard data be in a model and provide access to that model so that you could set its Model.modelData object or its properties.

To be clear, I don’t think copying some nodes and links should copy any shared data.

Hi,

That’s not really the issue that I am trying to explain here. If it’s not in the model, that’s fine. However, for me it is strange that it returns the nodeData instead of the modelData while we have specified the ofModel(). The only way to check for me now is to check whether the desired props are undefined. However, when the nodeData holds the same props (by coincidence) as the modelData, this check would fail.

If there is no model, what could one expect to happen with a Binding that is ofModel?

Return undefined or null instead of the nodeData?

I think for the next release we’ll improve the behavior so that ofModel Bindings are ignored if there is no Model.

That would make sense. Thanks. For now we will create a work-around.