UNPKG

37.5 kBHTMLView Raw
1<!DOCTYPE html>
2<html lang="en">
3 <head>
4<meta charset="utf-8"/>
5<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, viewport-fit=cover"/>
6
7<link rel="stylesheet" href="../assets/css/style.css"/>
8<!-- Copyright 1998-2021 by Northwoods Software Corporation. --> <title> GoJS Using Models -- Northwoods Software </title>
9 <link rel="stylesheet" href="../assets/css/prism.css" />
10 </head>
11 <script>
12
13 window.diagrams = [];
14 window.goCode = function(pre, w, h, parentid, animation) {
15 window.diagrams.push([pre, w, h, parentid, animation]);
16 }
17 </script>
18 <body>
19 <nav id="navTop" class="w-full z-30 top-0 text-white bg-nwoods-primary">
20 <div class="w-full container max-w-screen-lg mx-auto flex flex-wrap sm:flex-nowrap items-center justify-between mt-0 py-2">
21 <div class="md:pl-4">
22 <a class="text-white hover:text-white no-underline hover:no-underline
23 font-bold text-2xl lg:text-4xl rounded-lg hover:bg-nwoods-secondary " href="../">
24 <h1 class="mb-0 p-1 ">GoJS</h1>
25 </a>
26 </div>
27 <button id="topnavButton" class="rounded-lg sm:hidden focus:outline-none focus:ring" aria-label="Navigation">
28 <svg fill="currentColor" viewBox="0 0 20 20" class="w-6 h-6">
29 <path id="topnavOpen" fill-rule="evenodd" d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM9 15a1 1 0 011-1h6a1 1 0 110 2h-6a1 1 0 01-1-1z" clip-rule="evenodd"></path>
30 <path id="topnavClosed" class="hidden" fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path>
31 </svg>
32 </button>
33 <div id="topnavList" class="hidden sm:block items-center w-auto mt-0 text-white p-0 z-20">
34 <ul class="list-reset list-none font-semibold flex justify-end flex-wrap sm:flex-nowrap items-center px-0 pb-0">
35 <li class="p-1 sm:p-0"><a class="topnav-link" href="../learn/">Learn</a></li>
36 <li class="p-1 sm:p-0"><a class="topnav-link" href="../samples/">Samples</a></li>
37 <li class="p-1 sm:p-0"><a class="topnav-link" href="../intro/">Intro</a></li>
38 <li class="p-1 sm:p-0"><a class="topnav-link" href="../api/">API</a></li>
39 <li class="p-1 sm:p-0"><a class="topnav-link" href="https://www.nwoods.com/products/register.html">Register</a></li>
40 <li class="p-1 sm:p-0"><a class="topnav-link" href="../download.html">Download</a></li>
41 <li class="p-1 sm:p-0"><a class="topnav-link" href="https://forum.nwoods.com/c/gojs/11">Forum</a></li>
42 <li class="p-1 sm:p-0"><a class="topnav-link" href="https://www.nwoods.com/contact.html"
43 target="_blank" rel="noopener" onclick="getOutboundLink('https://www.nwoods.com/contact.html', 'contact');">Contact</a></li>
44 <li class="p-1 sm:p-0"><a class="topnav-link" href="https://www.nwoods.com/sales/index.html"
45 target="_blank" rel="noopener" onclick="getOutboundLink('https://www.nwoods.com/sales/index.html', 'buy');">Buy</a></li>
46 </ul>
47 </div>
48 </div>
49 <hr class="border-b border-gray-600 opacity-50 my-0 py-0" />
50 </nav>
51
52 <div class="md:flex flex-col md:flex-row md:min-h-screen w-full max-w-screen-xl mx-auto">
53
54 <div id="navSide" class="flex flex-col w-full md:w-40 lg:w-48 text-gray-700 bg-white flex-shrink-0">
55 <div class="flex-shrink-0 px-8 py-4">
56 <button id="navButton" class="rounded-lg md:hidden focus:outline-none focus:ring" aria-label="Navigation">
57 <svg fill="currentColor" viewBox="0 0 20 20" class="w-6 h-6">
58 <path id="navOpen" fill-rule="evenodd" d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM9 15a1 1 0 011-1h6a1 1 0 110 2h-6a1 1 0 01-1-1z" clip-rule="evenodd"></path>
59 <path id="navClosed" class="hidden" fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path>
60 </svg>
61 </button>
62 </div>
63 <nav id="navList" class="min-h-screen hidden md:block sidebar-nav flex-grow px-1 lg:px-4 pb-4 md:pb-0 md:overflow-y-auto break-words">
64 <a href="index.html">Basics</a>
65 <a href="buildingObjects.html">Building Parts</a>
66 <a href="usingModels.html">Using Models</a>
67 <a href="dataBinding.html">Data Binding</a>
68 <a href="react.html">GoJS with React</a>
69 <a href="angular.html">GoJS with Angular</a>
70 <a href="textBlocks.html">TextBlocks</a>
71 <a href="shapes.html">Shapes</a>
72 <a href="pictures.html">Pictures</a>
73 <a href="panels.html">Panels</a>
74 <a href="tablePanels.html">Table Panels</a>
75 <a href="brush.html">Brushes</a>
76 <a href="sizing.html">Sizing Objects</a>
77 <a href="itemArrays.html">Item Arrays</a>
78 <a href="changedEvents.html">Changed Events</a>
79 <a href="transactions.html">Transactions</a>
80 <a href="viewport.html">Coordinates</a>
81 <a href="initialView.html">Initial View</a>
82 <a href="collections.html">Collections</a>
83 <a href="links.html">Links</a>
84 <a href="linkLabels.html">Link Labels</a>
85 <a href="connectionPoints.html">Link Points</a>
86 <a href="ports.html">Ports</a>
87 <a href="nodes.html">Nodes</a>
88 <a href="debugging.html">Debugging</a>
89 <a href="layouts.html">Layouts</a>
90 <a href="trees.html">Trees</a>
91 <a href="subtrees.html">SubTrees</a>
92 <a href="groups.html">Groups</a>
93 <a href="subgraphs.html">SubGraphs</a>
94 <a href="sizedGroups.html">Sized Groups</a>
95 <a href="selection.html">Selection</a>
96 <a href="highlighting.html">Highlighting</a>
97 <a href="animation.html">Animation</a>
98 <a href="toolTips.html">ToolTips</a>
99 <a href="contextmenus.html">Context Menus</a>
100 <a href="events.html">Diagram Events</a>
101 <a href="tools.html">Tools</a>
102 <a href="commands.html">Commands</a>
103 <a href="permissions.html">Permissions</a>
104 <a href="validation.html">Validation</a>
105 <a href="HTMLInteraction.html">HTML Interaction</a>
106 <a href="layers.html">Layers &amp; Z-ordering</a>
107 <a href="palette.html">Palette</a>
108 <a href="overview.html">Overview</a>
109 <a href="resizing.html">Resizing Diagrams</a>
110 <a href="replacingDeleting.html">Replacing and Deleting</a>
111 <a href="buttons.html">Buttons</a>
112 <a href="templateMaps.html">Template Maps</a>
113 <a href="legends.html">Legends and Titles</a>
114 <a href="extensions.html">Extensions</a>
115 <a href="geometry.html">Geometry Strings</a>
116 <a href="grids.html">Grid Patterns</a>
117 <a href="graduatedPanels.html">Graduated Panels</a>
118 <a href="makingImages.html">Diagram Images</a>
119 <a href="makingSVG.html">Diagram SVG</a>
120 <a href="printing.html">Printing</a>
121 <a href="serverSideImages.html">Server-side Images</a>
122 <a href="nodeScript.html">GoJS in Node.js</a>
123 <a href="testing.html">Testing</a>
124 <a href="storage.html">Storage</a>
125 <a href="performance.html">Performance</a>
126 <a href="source.html">Building from Source</a>
127 <a href="platforms.html">Platforms</a>
128 <a href="deployment.html">Deployment</a>
129 </nav>
130 </div>
131 <div class="pt-4 px-2 md:px-0 lg:px-4 pb-16 w-full overflow-hidden">
132
133
134
135
136<h1>Using Models and Templates</h1>
137<p>
138You can build a diagram of nodes and links programmatically.
139But <b>GoJS</b> offers a way to build diagrams in a more declarative manner.
140You only provide the node and link data (i.e. the model) necessary for the diagram
141and instances of parts (i.e. the templates) that are automatically copied into the diagram.
142Those templates may be parameterized by properties of the node and link data.
143</p>
144
145<h2 id="BuildingDiagramsWithCode">Building diagrams with code</h2>
146<p>
147Let us try to build two nodes and connect them with a link.
148Here is one way of doing that:
149</p>
150<pre class="lang-js" id="twoNodesOneLinkCode"><code>
151 var node1 =
152 $(go.Node, "Auto",
153 $(go.Shape,
154 { figure: "RoundedRectangle",
155 fill: "lightblue" }),
156 $(go.TextBlock,
157 { text: "Alpha",
158 margin: 5 })
159 )
160 diagram.add(node1);
161
162 var node2 =
163 $(go.Node, "Auto",
164 $(go.Shape,
165 { figure: "RoundedRectangle",
166 fill: "pink" }),
167 $(go.TextBlock,
168 { text: "Beta",
169 margin: 5 })
170 );
171 diagram.add(node2);
172
173 diagram.add(
174 $(go.Link,
175 { fromNode: node1, toNode: node2 },
176 $(go.Shape)
177 ));
178</code></pre>
179<script>goCode("twoNodesOneLinkCode", 250, 150)</script>
180<p>
181This produces a nice, simple diagram.
182If you drag one of the nodes, you will see that the link remains connected to it.
183</p>
184<p>
185Although this way of building a diagram will work, it will not scale up well when creating large diagrams.
186Normally you will want a varying number of nodes each of which is very similar to the others.
187It would be better to share the construction of the node but parameterize a few things where the values should vary.
188</p>
189<p>
190One possibility would be put the code to build a Node into a function that returned a fully constructed Node,
191including all of the Panels and other GraphObjects in its visual tree.
192You would probably want to parameterize the function in order to provide the desired strings and colors and figures and image URLs.
193However such an approach is very ad-hoc: it would be difficult for the system to know how to automatically call such functions
194in order to create new nodes or new links on demand.
195Furthermore as your application data changes dynamically, how would you use such functions to update properties
196of existing objects within existing nodes and links, without inefficiently re-creating everything?
197And if you wanted anything/everything to update automatically as your application data changes,
198how would the system know what to do?
199</p>
200<p>
201This diagram-building code is also more cumbersome than it needs to be
202to manage references to nodes so that you can link them up.
203This is similar to the earlier problem when building a node's visual tree in code
204of having to use temporary named variables and referring to them when needed.
205</p>
206<p>
207What we are looking for is the separation of the appearance, definition, and construction
208of all of the nodes from the application data needed to describe the unique aspects of each particular node.
209</p>
210
211<h2 id="UsingModelAndTemplates">Using a Model and Templates</h2>
212<p>
213One way of achieving the separation of node appearance from node data is to use a data model and node templates.
214A model is basically just a collection of data that holds the essential information for each node and each link.
215A template is basically just a <a>Part</a> that can be copied; you would have different templates for <a>Node</a>s and for <a>Link</a>s.
216</p>
217<p>
218In fact, a <a>Diagram</a> already has very simple default templates for Nodes and Links.
219If you want to customize the appearance of the nodes in your diagram,
220you can replace the default node template by setting <a>Diagram.nodeTemplate</a>.
221</p>
222<p>
223To automatically make use of templates, provide the diagram a model holding the data for each node and the data for each link.
224A <a>GraphLinksModel</a> holds the collections (actually arrays) of node data and link data as the values of
225<a>GraphLinksModel.nodeDataArray</a> and <a>GraphLinksModel.linkDataArray</a>.
226You then set the <a>Diagram.model</a> property so that the diagram can create <a>Node</a>s for all of the node data
227and <a>Link</a>s for all of the link data.
228</p>
229<p>
230Models interpret and maintain references between the data.
231Each node data is expected to have a unique key value so that references to node data can be resolved reliably.
232Models also manage dynamically adding and removing data.
233</p>
234<p>
235The node data and the link data in models can be any JavaScript object.
236You get to decide what properties those objects have -- add as many as you need for your app.
237Since this is JavaScript, you can even add properties dynamically.
238There are several properties that <b>GoJS</b> models assume exist on the data,
239such as "key" (on node data) and "category" and "from" and "to" (the latter two on link data).
240However you can tell the model to use different property names by setting the model
241properties whose names end in "...Property".
242</p>
243<p>
244A node data object normally has its node's unique key value in the "key" property.
245Currently node data keys must be strings or numbers.
246You can get the key for a Node either via the <a>Node.key</a> property or via <code>someNode.data.key</code>.
247</p>
248<p>
249Let us create a diagram providing the minimal amount of necessary information.
250The particular node data has been put into an array of JavaScript objects.
251We declare the link relationships in a separate array of link data objects.
252Each link data holds references to the node data by using their keys.
253Normally the references are the values of the "from" and "to" properties.
254</p>
255<pre class="lang-js" id="simpleModelNoTemplates"><code>
256 var nodeDataArray = [
257 { key: "Alpha"},
258 { key: "Beta" }
259 ];
260 var linkDataArray = [
261 { from: "Alpha", to: "Beta" }
262 ];
263 diagram.model = new go.GraphLinksModel(nodeDataArray, linkDataArray);
264</code></pre>
265<script>goCode("simpleModelNoTemplates", 250, 150)</script>
266<p>
267This results in two nodes and a link, but the nodes do not appear the way we want.
268So we define the node template to be a generalization of the particular node constructions that we did above.
269</p>
270<pre class="lang-js" id="simpleModelNoBind"><code>
271 diagram.nodeTemplate = // provide custom Node appearance
272 $(go.Node, "Auto",
273 $(go.Shape,
274 { figure: "RoundedRectangle",
275 fill: "white" }),
276 $(go.TextBlock,
277 { text: "hello!",
278 margin: 5 })
279 );
280
281 var nodeDataArray = [
282 { key: "Alpha" },
283 { key: "Beta" }
284 ];
285 var linkDataArray = [
286 { from: "Alpha", to: "Beta" }
287 ];
288 diagram.model = new go.GraphLinksModel(nodeDataArray, linkDataArray);
289</code></pre>
290<script>goCode("simpleModelNoBind", 250, 150)</script>
291<p>
292Now the graph looks better, but the nodes have not been parameterized -- they are all identical!
293We can achieve that parameterization by using data binding.
294</p>
295
296<h2 id="ParameterizingNodesUsingDataBindings">Parameterizing Nodes using data binding</h2>
297<p>
298A data binding is a declarative statement that the value of the property of one object
299should be used to set the value of a property of another object.
300</p>
301<p>
302In this case, we want to make sure that the <a>TextBlock.text</a> property gets the
303"key" value of the corresponding node data.
304And we want to make sure that the <a>Shape.fill</a> property gets set to the color/brush given
305by the "color" property value of the corresponding node data.
306</p>
307<p>
308We can declare such data-bindings by creating <a>Binding</a> objects and associating them with the target <a>GraphObject</a>.
309Programmatically you do this by calling <a>GraphObject.bind</a>.
310But when using <b>go.GraphObject.make</b>, this happens automatically when you pass in a <a>Binding</a>.
311</p>
312<pre class="lang-js" id="simpleModelWithBind"><code>
313 diagram.nodeTemplate =
314 $(go.Node, "Auto",
315 $(go.Shape,
316 { figure: "RoundedRectangle",
317 fill: "white" }, // default Shape.fill value
318 new go.Binding("fill", "color")), // binding to get fill from nodedata.color
319 $(go.TextBlock,
320 { margin: 5 },
321 new go.Binding("text", "key")) // binding to get TextBlock.text from nodedata.key
322 );
323
324 var nodeDataArray = [
325 { key: "Alpha", color: "lightblue" }, // note extra property for each node data: color
326 { key: "Beta", color: "pink" }
327 ];
328 var linkDataArray = [
329 { from: "Alpha", to: "Beta" }
330 ];
331 diagram.model = new go.GraphLinksModel(nodeDataArray, linkDataArray);
332</code></pre>
333<script>goCode("simpleModelWithBind", 250, 150)</script>
334<p>
335Now we have the same diagram result as before, but it is implemented in much more general manner.
336You can easily add more node and link data to build bigger diagrams.
337And you can easily change the appearance of all of the nodes without modifying the data.
338</p>
339<p>
340Actually, you may notice that the <a>Link</a> is different: it has an arrowhead.
341No arrowhead was included when we first built this diagram using code.
342But the default <a>Diagram.linkTemplate</a> includes an arrowhead
343and we did not replace the link template with a custom one in this example.
344</p>
345<p>
346Notice that the value of <a>Shape.fill</a> in the template above gets a value twice.
347First it is set to "white". Then the binding sets it to whatever value the node data's "color" property has.
348It may be useful to be able to specify an initial value that remains in case the node data does
349not have a "color" property or if there is an error getting that value.
350</p>
351<p>
352At this point we can also be a bit more precise about what a template is.
353A template is a <a>Part</a> that may have some data <a>Binding</a>s and that is not itself in a diagram
354but may be copied to create parts that are added to a diagram.
355</p>
356
357<h3 id="TemplateDefinitions">Template Definitions</h3>
358<p>
359The implementations of all predefined templates are provided in <a href="../extensions/Templates.js">Templates.js</a> in the Extensions directory.
360You may wish to copy and adapt these definitions when creating your own templates.
361</p>
362<p>
363Those definitions might not be an up-to-date description
364of the actual standard template implementations that are in <b>GoJS</b>.
365</p>
366
367<h2 id="KindsOfModels">Kinds of Models</h2>
368<p>
369A model is a way of interpreting a collection of data objects as an abstract graph
370with various kinds of relationships determined by data properties and the assumptions that the model makes.
371The simplest kind of model, <a>Model</a>, can only hold "parts" without any relationships between them --
372no links or groups. But that model class acts as the base class for other kinds of models.
373</p>
374<h3 id="GraphLinksModel">GraphLinksModel</h3>
375<p>
376The kind of model you have seen above, <a>GraphLinksModel</a>, is actually the most general kind.
377It supports link relationships using a separate link data object for each <a>Link</a>.
378There is no inherent limitation on which <a>Nodes</a> a Link may connect, so reflexive and duplicate links are allowed.
379Links might also result in cycles in the graph.
380However you may prevent the user from drawing such links by setting various properties, such as <a>Diagram.validCycle</a>.
381And if you want to have a link appear to connect with a link rather than with a node,
382this is possible by having special nodes, known as "label nodes", that belong to links and are arranged along the
383path of a link in the same manner as text labels are arranged on a link.
384</p>
385<p>
386Furthermore a <a>GraphLinksModel</a> also supports identifying logically and physically different connection objects,
387known as "ports", within a <a>Node</a>.
388Thus an individual link may connect with a particular port rather than with the node as a whole.
389The <a href="connectionPoints.html">Link Points</a> and <a href="ports.html">Ports</a> pages discuss this topic in more depth.
390</p>
391<p>
392A <a>GraphLinksModel</a> also supports the group-membership relationship.
393Any <a>Part</a> can belong to at most one <a>Group</a>; no group can be contained in itself, directly or indirectly.
394You can learn more about grouping in other pages, such as <a href="groups.html">Groups</a>.
395</p>
396<h3 id="TreeModel">TreeModel</h3>
397<p>
398A simpler kind of model, the <a>TreeModel</a>, only supports link relationships that form a tree-structured graph.
399There is no separate link data, so there is no "linkDataArray".
400The parent-child relationship inherent in trees is determined by an extra property on the child node data which refers to the parent node by its key.
401If that property, whose name defaults to "parent", is undefined, then that data's corresponding node is a tree root.
402Each <a>Link</a> is still data bound, but the link's data is the child node data.
403</p>
404<pre class="lang-js" id="simpleTree"><code>
405 diagram.nodeTemplate =
406 $(go.Node, "Auto",
407 $(go.Shape,
408 { figure: "Ellipse" },
409 new go.Binding("fill", "color")),
410 $(go.TextBlock,
411 { margin: 5 },
412 new go.Binding("text", "key"))
413 );
414
415 var nodeDataArray = [
416 { key: "Alpha", color: "lightblue" },
417 { key: "Beta", parent: "Alpha", color: "yellow" }, // note the "parent" property
418 { key: "Gamma", parent: "Alpha", color: "orange" },
419 { key: "Delta", parent: "Alpha", color: "lightgreen" }
420 ];
421 diagram.model = new go.TreeModel(nodeDataArray);
422</code></pre>
423<script>goCode("simpleTree", 250, 150)</script>
424<p>
425Many of the tree-oriented samples make use of a TreeModel instead of a GraphLinksModel.
426But just because your graph is tree-structured does not mean you have to use a TreeModel.
427You may find that your data is organized with a separate "table" defining the link relationships,
428so that using a GraphLinksModel is most natural.
429Or you may want to use other features that TreeModel does not support.
430</p>
431<p>
432Other pages such as <a href="trees.html">Trees</a> discuss tree-oriented features of <b>GoJS</b> in more detail.
433</p>
434
435<h2 id="IdentityAndReferences">Identity and References</h2>
436<p>
437 Each <a>Node</a> is the visual representation of a specific JavaScript Object that is in the <a>Model.nodeDataArray</a>.
438 If there are two objects in the model, they will result in two nodes, even if the properties of both objects are exactly the same.
439 For example:
440</p>
441<pre class="lang-js"><code>
442 myDiagram.model.nodeDataArray = [
443 { text: "something", count: 17 },
444 { text: "something", count: 17 }
445 ];
446</code></pre>
447<p>
448 This will cause there to be two separate nodes that happen to have the same property values.
449 In fact each of those JavaScript Objects will get a different "key" value, so that references to nodes
450 will always be able to be distinguished.
451</p>
452<p>
453 This illustrates how the identity of each node is determined by the Object in memory that is the node's data.
454 You cannot delete a node by removing an object that is similar to one that is in the model.
455 Consider this statement:
456</p>
457<pre class="lang-js"><code>
458 myDiagram.model.removeNodeData({ text: "something", count: 17 });
459</code></pre>
460<p>
461 Such code will never remove any node data from the <a>Model.nodeDataArray</a> nor any <a>Node</a> from any <a>Diagram</a>
462 because the Object that is passed to <a>Model.removeNodeData</a> is a new Object, not the <em>same Object</em> that is present in the model.
463</p>
464<p>
465 Nor can you find a node by giving it a similar node data object.
466 There is no such method on <a>Model</a>, although there is a <a>Model.findNodeDataForKey</a> method.
467 But if you really want to search for nodes that have particular properties,
468 you can call <a>Diagram.findNodesByExample</a>.
469</p>
470<pre class="lang-js"><code>
471 var nodes = myDiagram.findNodesByExample({ text: "something", count: 17 });
472 nodes.each(function(n) { console.log(n.key); });
473</code></pre>
474<p>
475 For the model shown above, this will return a collection of two <a>Node</a>s.
476 It then iterates over that collection and prints each <a>Node.key</a>,
477 which in the case of the above model will be some automatically assigned key values.
478</p>
479
480<h3 id="ReferencesToNodes">References to Nodes</h3>
481<p>
482 Although the identity of a node is the node's data object in the model, references to nodes are not "pointers" to those objects.
483 Instead, references are always by the "key" of the node data.
484 (The property need not be named "key" -- see <a>Model.nodeKeyProperty</a>.)
485 Using keys instead of direct references to data objects makes it easier to read and write models,
486 especially by <a>Model.toJson</a> and <a>Model,fromJson</a>, and to debug them in memory.
487 Thus <a>Link</a>s are defined by data using keys, and <a>Group</a> membership is determined using keys:
488</p>
489<pre class="lang-js"><code>
490 myDiagram.model.nodeDataArray = [ // for a GraphLinksModel
491 { key: "Alpha" },
492 { key: "Beta", group: "Gamma" },
493 { key: "Gamma", isGroup: true }
494 ];
495 myDiagram.model.linkDataArray = [ // for a GraphLinksModel
496 { from: "Alpha", to: "Beta"}
497 ];
498</code></pre>
499<pre class="lang-js"><code>
500 myDiagram.model.nodeDataArray = [ // for a TreeModel
501 { key: "Alpha" },
502 { key: "Beta", parent: "Alpha" }
503 ];
504</code></pre>
505
506<h2 id="ModifyingModels">Modifying Models</h2>
507<p>
508If you want to add or remove nodes programmatically, you will probably want to call the
509<a>Model.addNodeData</a> and <a>Model.removeNodeData</a> methods.
510Use the <a>Model.findNodeDataForKey</a> method to find a particular node data object if you only have its unique key value.
511You may also call <a>Model.copyNodeData</a> to make a copy of a node data object that you can then modify and pass to <a>Model.addNodeData</a>.
512</p>
513<p>
514It does not work to simply mutate the Array that is the value of <a>Model.nodeDataArray</a>,
515because the <b>GoJS</b> software will not be notified about any change to any JavaScript Array and
516thus will not have a chance to add or remove <a>Node</a>s or other <a>Part</a>s as needed.
517(But setting the <a>Model.nodeDataArray</a> property to refer to a different Array does of course notify the model.)
518</p>
519<p>
520Similarly, it does not work to simply set a property of a node data object.
521Any <a>Binding</a> that depends on the property will not be notified about any changes,
522so it will not be able to update its target <a>GraphObject</a> property.
523For example, setting the color property will not cause the <a>Shape</a> to change color.
524</p>
525<pre class="lang-js"><code>
526 var data = myDiagram.model.findNodeDataForKey("Delta");
527 // This will NOT change the color of the "Delta" Node
528 if (data !== null) data.color = "red";
529</code></pre>
530<p>
531Instead you need to call <a>Model.setDataProperty</a> to modify an object in the model.
532</p>
533<pre class="lang-js"><code>
534 var data = myDiagram.model.findNodeDataForKey("Delta");
535 // This will update the color of the "Delta" Node
536 if (data !== null) myDiagram.model.setDataProperty(data, "color", "red");
537</code></pre>
538<p>
539Calling model methods such as <a>Model.addNodeData</a> or <a>Model.setDataProperty</a> is required
540when the JavaScript Array or Object is already part of the Model.
541When first building the Array of Objects for the <a>Model.nodeDataArray</a>
542or when initializing a JavaScript Object as a new node data object, such calls are not necessary.
543But once the data is part of the Model, calling the model's methods to effect changes is necessary.
544</p>
545
546<h3 id="ExternallyModifiedData">Externally Modified Data</h3>
547<p>
548 In some software architectures it might not be possible to insist that all data changes go through <a>Model</a> methods.
549 In such cases it is possible to call <a>Diagram.updateAllRelationshipsFromData</a> and
550 <a>Diagram.updateAllTargetBindings</a>.
551</p>
552<p>
553 However, please note that doing so will prevent the <a>UndoManager</a> from properly recording state changes.
554 There would be no way for the <a>UndoManager</a> to know what had been the previous values of properties.
555 Furthermore it makes it hard to have more than one Diagram showing the Model.
556</p>
557
558<h3 id="ImmutableData">Immutable Data</h3>
559<p>
560 In some software architectures it is customary to have "models" consist of immutable (unmodifiable) data.
561 However, as the GoJS diagram is modified, its model data will be modified, so you cannot use that immutable data in the model.
562 You could make a copy of all of the immutable data and then replace the <a>Diagram.model</a> whenever the data
563 has changed outside of the diagram/model. But that would cause old Nodes and Links to be re-created,
564 and that would be unworkably expensive in time and space when the model is large.
565</p>
566<p>
567 If you do have immutable model data, you can update the existing <a>Model</a> and thus its <a>Diagram</a>s by calling
568 the <a>Model.mergeNodeDataArray</a> and <a>GraphLinksModel.mergeLinkDataArray</a> methods.
569 This will be much more efficient than replacing the <a>Model.nodeDataArray</a> and <a>GraphLinksModel.linkDataArray</a>
570 Arrays each time, because it will preserve the existing Nodes and Links if possible.
571</p>
572<p>
573 Note that this scheme depends on maintaining the "key"s for all of the node data and for all of the link data.
574 That happens automatically for all nodes, but for GraphLinksModels, it means setting
575 <a>GraphLinksModel.linkKeyProperty</a> to the name of the property on the link data that
576 you want to use to remember the key value.
577</p>
578<p>
579 After each diagram transaction some of the model data may have changed.
580 But you cannot share references to that modified data with the rest of the software that is expecting immutable data.
581 Instead you can call <a>Model.toIncrementalData</a> which will provide copies of the modified data.
582 That data can then be used to update the rest of the app's state.
583 Read more about this at <a href="react.html">Using GoJS with React</a>
584 and the <a href="https://github.com/NorthwoodsSoftware/gojs-react">gojs-react package</a>,
585 which provides generic Diagram components that you can use in your app using React.
586</p>
587
588<h2 id="SavingAndLoadingModels">Saving and Loading Models</h2>
589<p>
590<b>GoJS</b> does not require you to save models in any particular medium or format.
591But because this is JavaScript and JSON is the most popular data-interchange format,
592we do make it easy to write and read models as text in JSON format.
593</p>
594<p>
595Just call <a>Model.toJson</a> to generate a string representing your model.
596Call the static method <a>Model,fromJson</a> to construct and initialize a model given a string produced by <a>Model.toJson</a>.
597Many of the samples demonstrate this -- search for JavaScript functions named "save" and "load".
598Most of those functions write and read a TextArea on the page itself, so that you can see and modify the JSON text and then load it to get a new diagram.
599But please be cautious when editing because JSON syntax is very strict, and any syntax errors will cause those "load" functions to fail.
600</p>
601<p>
602JSON formatted text has strict limits on the kinds of data that you can represent without additional assumptions.
603To save and load any data properties that you set on your node data (or link data), they need to meet the following requirements:
604</p>
605<ul>
606 <li>the property is enumerable and its name does not start with an underscore (you can use property names that do start with an underscore, but they won't be saved)</li>
607 <li>the property value is not undefined and is not a function (JSON cannot faithfully hold functions)</li>
608 <li>the model knows how to convert the property value to JSON format (numbers, strings, JavaScript Arrays, or plain JavaScript Objects)</li>
609 <li>property values that are Objects or Arrays form a tree structure -- no shared or cyclical references</li>
610</ul>
611<p>
612<a>Model.toJson</a> and <a>Model,fromJson</a> will also handle instances of
613<a>Point</a>, <a>Size</a>, <a>Rect</a>, <a>Spot</a>, <a>Margin</a>, <a>Geometry</a>, and non-pattern <a>Brush</a>es.
614However we recommend that you store those objects in their string representations, using those classes' <code>parse</code> and <code>stringify</code> static functions.
615</p>
616<p>
617Because you are using JavaScript, it is trivial for you to add data properties to your node data.
618This allows you to associate whatever information you need with each node.
619But if you need to associate some information with the model, which will be present even if there is no node data at all,
620you can add properties to the <a>Model.modelData</a> object.
621This object's properties will be written by <a>Model.toJson</a> and read by <a>Model,fromJson</a>, just as node data objects are written and read.
622</p>
623
624 </div>
625 </div>
626
627 <div class="bg-nwoods-primary">
628 <section class="max-w-screen-lg text-white container mx-auto py-2 px-12">
629 <p id="version" class="leading-none mb-2 my-4">GoJS</p>
630 </section>
631 </div><footer class="bg-nwoods-primary text-white">
632 <div class="container max-w-screen-lg mx-auto px-8">
633 <div class="w-full py-6">
634
635 <div class="max-w-screen-lg xl:max-w-screen-xl mx-auto px-4 sm:px-6 md:px-8">
636 <ul class="text-sm font-medium pb-14 sm:pb-20 grid grid-cols-1 sm:grid-cols-3 gap-y-10">
637 <li class="list-none row-span-2">
638 <h2 class="text-base font-semibold tracking-wide">GoJS</h2>
639 <ul class="list-none space-y-4 md:space-y-1 px-0">
640 <li>
641 <a href="../samples/index.html">Samples</a>
642 </li>
643 <li>
644 <a href="../learn/index.html">Learn</a>
645 </li>
646 <li>
647 <a href="../intro/index.html">Intro</a>
648 </li>
649 <li>
650 <a href="../api/index.html">API</a>
651 </li>
652 <li>
653 <a href="../changelog.html">Changelog</a>
654 </li>
655 <li>
656 <a href="https://github.com/NorthwoodsSoftware/GoJS">GitHub</a>
657 </li>
658 </ul>
659 </li>
660 <li class="list-none row-span-2">
661 <h2 class="text-base font-semibold tracking-wide">Support</h2>
662 <ul class="list-none space-y-4 md:space-y-1 px-0">
663 <li>
664 <a href="https://www.nwoods.com/contact.html"
665 target="_blank" rel="noopener" onclick="getOutboundLink('https://www.nwoods.com/contact.html', 'contact');">Contact</a>
666 </li>
667 <li>
668 <a href="https://forum.nwoods.com/c/gojs">Forum</a>
669 </li>
670 <li>
671 <a href="https://www.nwoods.com/app/activate.aspx?sku=gojs">Activate</a>
672 </li>
673 <li>
674 <a href="https://www.nwoods.com/sales/index.html"
675 target="_blank" rel="noopener" onclick="getOutboundLink('https://www.nwoods.com/sales/index.html', 'buy');">Buy</a>
676 </li>
677 <li>
678 <a href="https://www.youtube.com/channel/UC9We8EoX596-6XFjJDtZIDg">Videos</a>
679 </li>
680 </ul>
681 </li>
682 <li class="list-none row-span-2">
683 <h2 class="text-base font-semibold tracking-wide">Company</h2>
684 <ul class="list-none space-y-4 md:space-y-1 px-0">
685 <li>
686 <a href="https://www.nwoods.com">Northwoods</a>
687 </li>
688 <li>
689 <a href="https://www.nwoods.com/about.html">About Us</a>
690 </li>
691 <li>
692 <a href="https://www.nwoods.com/contact.html">Contact Us</a>
693 </li>
694 <li>
695 <a href="https://twitter.com/northwoodsgo">Twitter</a>
696 </li>
697
698 </ul>
699 </li>
700 </ul>
701
702
703 <p class="text-sm text-gray-100 md:mb-6">
704 Copyright 1998-2021 <a class="text-white" href="https://www.nwoods.com">Northwoods Software</a>
705 </p>
706 </div>
707 </div>
708</footer> </body>
709
710<script async src="https://www.googletagmanager.com/gtag/js?id=UA-1506307-5"></script>
711<script>
712 window.dataLayer = window.dataLayer || [];
713 function gtag(){dataLayer.push(arguments);}
714 gtag('js', new Date()); gtag('config', 'UA-1506307-5');
715 var getOutboundLink = function(url, label) {
716 gtag('event', 'click', {
717 'event_category': 'outbound',
718 'event_label': label,
719 'transport_type': 'beacon'
720 });
721 }
722
723 // topnav
724 var topButton = document.getElementById("topnavButton");
725 var topnavList = document.getElementById("topnavList");
726 topButton.addEventListener("click", function() {
727 this.classList.toggle("active");
728 topnavList.classList.toggle("hidden");
729 document.getElementById("topnavOpen").classList.toggle("hidden");
730 document.getElementById("topnavClosed").classList.toggle("hidden");
731 });
732</script>
733 <script src="../assets/js/prism.js"></script>
734 <script src="../release/go.js"></script>
735 <script src="../assets/js/goDoc.js"></script>
736 <script>
737 document.addEventListener("DOMContentLoaded", function() {
738 if (window.go) document.getElementById('version').textContent = "GoJS version " + go.version;
739 if (window.goDoc) window.goDoc();
740 var d = window.diagrams;
741 for (var i = 0; i < d.length; i++) {
742 var dargs = d[i];
743 goCodeExecute(dargs[0], dargs[1], dargs[2], dargs[3], dargs[4]);
744 }
745 if (window.extra) window.extra();
746 });
747 </script>
748</html>