Is there a way to make the two way binding only update when a transaction has finished?
I am testing with a vue component that holds the state. If I pass this state directly to the nodeArray, the web application is not able to keep up when a node is moved:
<!DOCTYPE html>
<html>
<head>
<title>Performance two way binding</title>
<meta charset="UTF-8">
</head>
<body onload="init()">
<div id="app">
<span id="myNumberOfNodes"></span>
<div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:600px"></div>
<pre v-text="diagramData.nodeDataArray" style="height: 300px; overflow: scroll"></pre>
</div>
<script src="go.js"></script>
<script src="https://unpkg.com/vue"></script>
<script id="code">
function init() {
myApp = new Vue({
el: '#app',
data: {
diagramData: {
nodeDataArray: [],
linkDataArray: []
}
},
mounted: function () {
var $ = go.GraphObject.make; // for conciseness in defining templates
myDiagram =
$(go.Diagram, "myDiagramDiv", // create a Diagram for the DIV HTML element
{ // enable undo & redo
"undoManager.isEnabled": true
});
// define a simple Node template
myDiagram.nodeTemplate =
$(go.Node, "Auto", // the Shape will go around the TextBlock
$(go.Shape, "RoundedRectangle", { strokeWidth: 2, fill: "white" }),
$(go.TextBlock, { margin: 8 }, new go.Binding("text", "key")),
new go.Binding("location", "location", go.Point.parse).makeTwoWay(go.Point.stringify)
);
// but use the default Link template, by not setting Diagram.linkTemplate
// create the model data that will be represented by Nodes and Links
let numberOfNodes = 50000
let width = parseInt(Math.sqrt(numberOfNodes))
let spacing = 100
for (let i = 0; i < numberOfNodes; ++i) {
this.diagramData.nodeDataArray.push({ key: `Alpha${i}`, "location": `${(i % width) * spacing} ${parseInt(i / width) * spacing}` })
this.diagramData.linkDataArray.push({ from: `Alpha${i}`, to: `Alpha${i + 1}` })
}
myDiagram.model = new go.GraphLinksModel(this.diagramData.nodeDataArray, this.diagramData.linkDataArray);
}
})
}
</script>
</body>
</html>
However, when I subscribe to the changed event and only update the vue state when a transaction is finished, it seems to work fine:
<!DOCTYPE html>
<html>
<head>
<title>Performance on transaction finished update</title>
<meta charset="UTF-8">
</head>
<body onload="init()">
<div id="app">
<span id="myNumberOfNodes"></span>
<div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:600px"></div>
<pre v-text="savedDiagramData.nodeDataArray" style="height: 300px; overflow: scroll"></pre>
</div>
<script src="go.js"></script>
<script src="https://unpkg.com/vue"></script>
<script id="code">
function init() {
myApp = new Vue({
el: '#app',
data: {
savedDiagramData: {
nodeDataArray: [],
linkDataArray: []
}
},
mounted: function () {
var $ = go.GraphObject.make; // for conciseness in defining templates
var app = this
myDiagram =
$(go.Diagram, "myDiagramDiv", // create a Diagram for the DIV HTML element
{ // enab le undo & redo
"undoManager.isEnabled": true,
"ModelChanged": function (e) {
if (e.isTransactionFinished) { // show the model data in the page's TextArea
// console.log('hi')
// app.savedDiagramData = JSON.parse(e.model.toJson())
increment = JSON.parse(myDiagram.model.toIncrementalJson(e))
if (increment.modifiedNodeData) {
increment.modifiedNodeData.forEach((d) => {
Object.assign(app.savedDiagramData.nodeDataArray[d.key], d)
})
}
}
}
});
// define a simple Node template
myDiagram.nodeTemplate =
$(go.Node, "Auto", // the Shape will go around the TextBlock
$(go.Shape, "RoundedRectangle", { strokeWidth: 2, fill: "white" }),
$(go.TextBlock, { margin: 8, editable: true }, new go.Binding("text", "key")),
new go.Binding("location", "location", go.Point.parse).makeTwoWay(go.Point.stringify)
);
// but use the default Link template, by not setting Diagram.linkTemplate
// create the model data that will be represented by Nodes and Links
let numberOfNodes = 50000
let width = parseInt(Math.sqrt(numberOfNodes))
let spacing = 100
for (let i = 0; i < numberOfNodes; ++i) {
this.savedDiagramData.nodeDataArray.push({ key: i, "location": `${(i % width) * spacing} ${parseInt(i / width) * spacing}` })
this.savedDiagramData.linkDataArray.push({ from: i, to: i+1 })
}
let initialSavedDiagramData = JSON.parse(JSON.stringify(this.savedDiagramData))
myDiagram.model = new go.GraphLinksModel(initialSavedDiagramData.nodeDataArray, initialSavedDiagramData.linkDataArray);
}
})
}
</script>
</body>
</html>
However, on the second option, I am not able to easily propagate the source state to the target state using updateTargetBindings
since the target and the source are not referring to the same data.
Is it possible to make the target to source update (twoWayBinding) somewhat lazy so that it only updates the state when a transaction has finished?
Thanks!