The loaded diagram is not the same as saved one

I think it’s expected to see the same diagram as the saved one, but in my case they are not the same. Please, take a look at the screen shots attached.

Before:

After:

What should we do to overcome this issue?

First, do all of your Nodes have a TwoWay Binding on Node.location?

Second, do you not set Diagram.layout, or do you set the layout but disable automatic layout on initialization by setting Layout.isInitial to false?

Could you tell me, please, what’s wrong with the following code that produced 2 different diagrams above?

var red = "orangered";  // 0 or false
var green = "forestgreen";  // 1 or true
var blue = "blue";
var aqua = "aqua";
var white = "white";
var dollarbill = "#85BB65";
var myDiagram;

function initElectricalShema() {
    //if (window.goSamples) goSamples();  // init for these samples -- you don't need to call this
    var $ = go.GraphObject.make;  // for conciseness in defining templates

    myDiagram =
      $(go.Diagram, "myDiagramDiv",  // create a new Diagram in the HTML DIV element "myDiagramDiv"
        {
            //initialContentAlignment: go.Spot.Center,
            allowDrop: true,  // Nodes from the Palette can be dropped into the Diagram
            //"draggingTool.isGridSnapEnabled": true,  // dragged nodes will snap to a grid of 10x10 cells
            "undoManager.isEnabled": true
        });

    //myDiagram.model = go.Model.fromJson("{ \"class\": \"go.GraphLinksModel\", \"nodeDataArray\": [], \"linkDataArray\": []}");
    //myDiagram.model = go.Model.fromJson("{ \"class\": \"go.GraphLinksModel\", \"nodeDataArray\": [ {\"category\":\"input\", \"key\":-1, \"loc\":\"-111.45001220703125 -39.91667175292969\"} ], \"linkDataArray\": []}");

    myDiagram.requestUpdate();

    // when the document is modified, add a "*" to the title and enable the "Save" button
    myDiagram.addDiagramListener("Modified", function (e) {
        var button = document.getElementById("saveModel");
        if (button) button.disabled = !myDiagram.isModified;
        var idx = document.title.indexOf("*");
        if (myDiagram.isModified) {
            if (idx < 0) document.title += "*";
        } else {
            if (idx >= 0) document.title = document.title.substr(0, idx);
        }
    });

    var palette = new go.Palette("palette");  // create a new Palette in the HTML DIV element "palette"

    // creates relinkable Links that will avoid crossing Nodes when possible and will jump over other Links in their paths
    myDiagram.linkTemplate =
      $(go.Link,
        {
            //routing: go.Link.AvoidsNodes,
            //curve: go.Link.JumpOver,
            //corner: 3,
            relinkableFrom: true, relinkableTo: true,
            selectionAdorned: false, // Links are not adorned when selected so that their color remains visible.
            shadowOffset: new go.Point(0, 0), shadowBlur: 5, shadowColor: "blue"
        },
        new go.Binding("isShadowed", "isSelected").ofObject(),
        $(go.Shape,
          { name: "SHAPE", strokeWidth: 2, stroke: red }));

    // node template helpers
    var sharedToolTip =
      $(go.Adornment, "Auto",
        $(go.Shape, "RoundedRectangle", { fill: "lightyellow" }),
        $(go.TextBlock, { margin: 2 },
          new go.Binding("text", "", function (d) { return d.category; })));

    // define some common property settings
    function nodeStyle() {
        return [new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
                new go.Binding("isShadowed", "isSelected").ofObject(),
                {
                    selectionAdorned: false,
                    shadowOffset: new go.Point(0, 0),
                    shadowBlur: 15,
                    shadowColor: "blue",
                    toolTip: sharedToolTip
                }];
    }

    function shapeStyle1() {
        return {
            name: "NODESHAPE",
            fill: "lightgray",
            stroke: "darkslategray",
            desiredSize: new go.Size(40, 40),
            strokeWidth: 2
        };
    }

    function shapeStyle2() {
        return {
            name: "NODESHAPE1",
            fill: "aqua",
            stroke: "yellow",
            desiredSize: new go.Size(40, 40),
            strokeWidth: 2
        };
    }

    function shapeStyle3() {
        return {
            name: "NODESHAPE2",
            fill: "dollarbill",
            stroke: "darkgreen",
            desiredSize: new go.Size(10, 40),
            strokeWidth: 2
        };
    }

    function shapeStyle4() {
        return {
            name: "NODESHAPE3",
            fill: "green",
            stroke: "green",
            desiredSize: new go.Size(1, 4),
            strokeWidth: 2
        };
    }

    function shapeStyle5() {
        return {
            name: "NODESHAPE4",
            fill: "green",
            stroke: "green",
            desiredSize: new go.Size(1, 10),
            strokeWidth: 2
        };
    }

    function portStyle(input) {
        return {
            desiredSize: new go.Size(6, 6),
            fill: "black",
            fromSpot: go.Spot.Right,
            fromLinkable: !input,
            toSpot: go.Spot.Left,
            toLinkable: input,
            toMaxLinks: 2,
            cursor: "pointer"
        };
    }

    function portStyle1(input) {
        return {
            desiredSize: new go.Size(2, 2),
            fill: "green",
            //fromSpot: go.Spot.Center,
            fromLinkable: input,
            //toSpot: go.Spot.Center,
            toLinkable: !input,
            toMaxLinks: 2,
            cursor: "pointer"
        };
    }

    function portStyle2(input) {
        return {
            desiredSize: new go.Size(2, 2),
            fill: "green",
            //fromSpot: go.Spot.Center,
            fromLinkable: !input,
            //toSpot: go.Spot.Center,
            toLinkable: input,
            toMaxLinks: 2,
            cursor: "pointer"
        };
    }

    // define templates for each type of node
    var inputTemplate =
        $(go.Node, "Spot",
        //{ guid: "12345" },
        //new go.Binding("guid", "number"),
        nodeStyle(),
        $(go.Shape, "Circle", shapeStyle1(),
            { fill: red }),  // override the default fill (from shapeStyle1()) to be red
        $(go.Shape, "Rectangle", portStyle(false),  // the only port
            { portId: "", alignment: new go.Spot(1, 0.5) }),
        { // if double-clicked, an input node will change its value, represented by the color.
            doubleClick: function (e, obj) {
                //e.diagram.startTransaction("Toggle Input");
                //var shp = obj.findObject("NODESHAPE");
                //shp.fill = (shp.fill === green) ? red : green;
                //updateStates();
                //e.diagram.commitTransaction("Toggle Input");
                window.open("http://www.microsoft.com", "_blank");
        }}
        );

    var outputTemplate =
      $(go.Node, "Spot", nodeStyle(),
        $(go.Shape, "Rectangle", shapeStyle1(),
          { fill: green }),  // override the default fill (from shapeStyle1()) to be green
        $(go.Shape, "Rectangle", portStyle(true),  // the only port
          { portId: "", alignment: new go.Spot(0, 0.5) })
        //{
        //    doubleclick: function (e, node) {
        //        window.open("http://www.microsoft.com", "_blank");
        //    }
        //}
      );

    var andTemplate =
      $(go.Node, "Spot", nodeStyle(),
        $(go.Shape, "AndGate", shapeStyle1()),
        $(go.Shape, "Rectangle", portStyle(true),
          { portId: "in1", alignment: new go.Spot(0, 0.3) }),
        $(go.Shape, "Rectangle", portStyle(true),
          { portId: "in2", alignment: new go.Spot(0, 0.7) }),
        $(go.Shape, "Rectangle", portStyle(false),
          { portId: "out", alignment: new go.Spot(1, 0.5) })
      );

    var orTemplate =
      $(go.Node, "Spot", nodeStyle(),
        $(go.Shape, "OrGate", shapeStyle1()),
        $(go.Shape, "Rectangle", portStyle(true),
          { portId: "in1", alignment: new go.Spot(0.16, 0.3) }),
        $(go.Shape, "Rectangle", portStyle(true),
          { portId: "in2", alignment: new go.Spot(0.16, 0.7) }),
        $(go.Shape, "Rectangle", portStyle(false),
          { portId: "out", alignment: new go.Spot(1, 0.5) })
      );

    var xorTemplate =
      $(go.Node, "Spot", nodeStyle(),
        $(go.Shape, "XorGate", shapeStyle1()),
        $(go.Shape, "Rectangle", portStyle(true),
          { portId: "in1", alignment: new go.Spot(0.26, 0.3) }),
        $(go.Shape, "Rectangle", portStyle(true),
          { portId: "in2", alignment: new go.Spot(0.26, 0.7) }),
        $(go.Shape, "Rectangle", portStyle(false),
          { portId: "out", alignment: new go.Spot(1, 0.5) })
      );

    var norTemplate =
      $(go.Node, "Spot", nodeStyle(),
        $(go.Shape, "NorGate", shapeStyle1()),
        $(go.Shape, "Rectangle", portStyle(true),
          { portId: "in1", alignment: new go.Spot(0.16, 0.3) }),
        $(go.Shape, "Rectangle", portStyle(true),
          { portId: "in2", alignment: new go.Spot(0.16, 0.7) }),
        $(go.Shape, "Rectangle", portStyle(false),
          { portId: "out", alignment: new go.Spot(1, 0.5) })
      );

    var xnorTemplate =
      $(go.Node, "Spot", nodeStyle(),
        $(go.Shape, "XnorGate", shapeStyle1()),
        $(go.Shape, "Rectangle", portStyle(true),
          { portId: "in1", alignment: new go.Spot(0.26, 0.3) }),
        $(go.Shape, "Rectangle", portStyle(true),
          { portId: "in2", alignment: new go.Spot(0.26, 0.7) }),
        $(go.Shape, "Rectangle", portStyle(false),
          { portId: "out", alignment: new go.Spot(1, 0.5) })
      );

    var nandTemplate =
      $(go.Node, "Spot", nodeStyle(),
        $(go.Shape, "NandGate", shapeStyle1()),
        $(go.Shape, "Rectangle", portStyle(true),
          { portId: "in1", alignment: new go.Spot(0, 0.3) }),
        $(go.Shape, "Rectangle", portStyle(true),
          { portId: "in2", alignment: new go.Spot(0, 0.7) }),
        $(go.Shape, "Rectangle", portStyle(false),
          { portId: "out", alignment: new go.Spot(1, 0.5) })
      );

    var notTemplate =
      $(go.Node, "Spot", nodeStyle(),
        $(go.Shape, "Inverter", shapeStyle1()),
        $(go.Shape, "Rectangle", portStyle(true),
          { portId: "in", alignment: new go.Spot(0, 0.5) }),
        $(go.Shape, "Rectangle", portStyle(false),
          { portId: "out", alignment: new go.Spot(1, 0.5) })
      );

    var custom1Template =
      $(go.Node, "Spot", nodeStyle(),
        $(go.Shape, "Rectangle", shapeStyle2(),
          { fill: blue }),  // override the default fill (from shapeStyle1()) to be green
        $(go.Shape, "Rectangle", portStyle(true),  // the only port
          { portId: "", alignment: new go.Spot(0, 0.5) })
      );

    var custom2Template =
      $(go.Node, "Vertical",
        $(go.Shape, "Circle", shapeStyle1(),
          { fill: white }),  // override the default fill (from shapeStyle1()) to be green
        $(go.Shape, "Rectangle", shapeStyle4(),
          { fill: green }),  // override the default fill (from shapeStyle1()) to be green
        $(go.Shape, "Rectangle", shapeStyle3(),
          { fill: dollarbill }, // override the default fill (from shapeStyle1()) to be green
          { visible: false }),
        $(go.Shape, "Rectangle", shapeStyle5(),
          { fill: green }),  // override the default fill (from shapeStyle1()) to be green
        $(go.Shape, "Rectangle", portStyle1(true),  // the only port
          { portId: "" }),
        $(go.Shape, "Rectangle", portStyle2(true),  // the only port
          { portId: "" }),
        { // if double-clicked, an input node will change its value, represented by the color.
            doubleClick: function (e, obj) {
                //e.diagram.startTransaction("Toggle Input");
                //var shp = obj.findObject("NODESHAPE");
                //shp.fill = (shp.fill === green) ? red : green;
                //updateStates();
                //e.diagram.commitTransaction("Toggle Input");
                window.open("http://www.microsoft.com", "_blank");
            }
        }
      );

    // add the templates created above to myDiagram and palette
    myDiagram.nodeTemplateMap.add("input", inputTemplate);
    myDiagram.nodeTemplateMap.add("output", outputTemplate);
    myDiagram.nodeTemplateMap.add("and", andTemplate);
    myDiagram.nodeTemplateMap.add("or", orTemplate);
    myDiagram.nodeTemplateMap.add("xor", xorTemplate);
    myDiagram.nodeTemplateMap.add("not", notTemplate);
    myDiagram.nodeTemplateMap.add("nand", nandTemplate);
    myDiagram.nodeTemplateMap.add("nor", norTemplate);
    myDiagram.nodeTemplateMap.add("xnor", xnorTemplate);
    myDiagram.nodeTemplateMap.add("custom1", custom1Template);
    myDiagram.nodeTemplateMap.add("custom2", custom2Template);

    // share the template map with the Palette
    palette.nodeTemplateMap = myDiagram.nodeTemplateMap;

    palette.model.nodeDataArray = [
      { category: "input" },
      { category: "output" },
      { category: "and" },
      { category: "or" },
      { category: "xor" },
      { category: "not" },
      { category: "nand" },
      { category: "nor" },
      { category: "xnor" },
      { category: "custom1" },
      { category: "custom2" }
];

    // load the initial diagram
    load();

    // continually update the diagram
    loop();
}

// update the diagram every 250 milliseconds
function loop() {
    setTimeout(function () { updateStates(); loop(); }, 250);
}

// update the value and appearance of each node according to its type and input values
function updateStates() {
    var oldskip = myDiagram.skipsUndoManager;
    myDiagram.skipsUndoManager = true;
    // do all "input" nodes first
    myDiagram.nodes.each(function (node) {
        if (node.category === "input") {
            doInput(node);
        }
    });
    // now we can do all other kinds of nodes
    myDiagram.nodes.each(function (node) {
        switch (node.category) {
            case "and": doAnd(node); break;
            case "or": doOr(node); break;
            case "xor": doXor(node); break;
            case "not": doNot(node); break;
            case "nand": doNand(node); break;
            case "nor": doNor(node); break;
            case "xnor": doXnor(node); break;
            case "output": doOutput(node); break;
            case "custom1": doCustom1(node); break;
            case "custom2": doCustom2(node); break;
            case "input": break;  // doInput already called, above
        }
    });
    myDiagram.skipsUndoManager = oldskip;
}

// helper predicate
function linkIsTrue(link) {  // assume the given Link has a Shape named "SHAPE"
    return link.findObject("SHAPE").stroke === green;
}

// helper function for propagating results
function setOutputLinks(node, color) {
    node.findLinksOutOf().each(function (link) { link.findObject("SHAPE").stroke = color; });
}

// update nodes by the specific function for its type
// determine the color of links coming out of this node based on those coming in and node type

function doInput(node) {
    // the output is just the node's Shape.fill
    setOutputLinks(node, node.findObject("NODESHAPE").fill);
}

function doAnd(node) {
    var color = node.findLinksInto().all(linkIsTrue) ? green : red;
    setOutputLinks(node, color);
}
function doNand(node) {
    var color = !node.findLinksInto().all(linkIsTrue) ? green : red;
    setOutputLinks(node, color);
}
function doNot(node) {
    var color = !node.findLinksInto().all(linkIsTrue) ? green : red;
    setOutputLinks(node, color);
}

function doOr(node) {
    var color = node.findLinksInto().any(linkIsTrue) ? green : red;
    setOutputLinks(node, color);
}
function doNor(node) {
    var color = !node.findLinksInto().any(linkIsTrue) ? green : red;
    setOutputLinks(node, color);
}

function doXor(node) {
    var truecount = 0;
    node.findLinksInto().each(function (link) { if (linkIsTrue(link)) truecount++; });
    var color = truecount % 2 === 0 ? green : red;
    setOutputLinks(node, color);
}
function doXnor(node) {
    var truecount = 0;
    node.findLinksInto().each(function (link) { if (linkIsTrue(link)) truecount++; });
    var color = truecount % 2 !== 0 ? green : red;
    setOutputLinks(node, color);
}

function doOutput(node) {
    // assume there is just one input link
    // we just need to update the node's Shape.fill
    node.linksConnected.each(function (link) { node.findObject("NODESHAPE").fill = link.findObject("SHAPE").stroke; });
}

function doCustom1(node) {
    // assume there is just one input link
    // we just need to update the node's Shape.fill
    node.linksConnected.each(function (link) { node.findObject("NODESHAPE1").fill = link.findObject("SHAPE").stroke; });
}

function doCustom2(node) {
    // assume there is just one input link
    // we just need to update the node's Shape.fill
    node.linksConnected.each(function (link) { node.findObject("NODESHAPE1").fill = link.findObject("SHAPE").stroke; });
}

// save a model to and load a model from JSON text, displayed below the Diagram
function save() {
    document.getElementById("mySavedModel").value = myDiagram.model.toJson();
    //alert(myDiagram.model.toJson());
    myDiagram.isModified = false;
}
function load() {
    myDiagram.model = go.Model.fromJson(document.getElementById("mySavedModel").value);
}

I don’t see a Node.location Binding on your custom2 template.

We really cannot debug your code for you.

We resolved this issue. I’ve found one of my custom node templates without nodeStyle() where was placed:

new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify)