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 | <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 & 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>
|
138 | You can build a diagram of nodes and links programmatically.
|
139 | But <b>GoJS</b> offers a way to build diagrams in a more declarative manner.
|
140 | You only provide the node and link data (i.e. the model) necessary for the diagram
|
141 | and instances of parts (i.e. the templates) that are automatically copied into the diagram.
|
142 | Those 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>
|
147 | Let us try to build two nodes and connect them with a link.
|
148 | Here 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>
|
181 | This produces a nice, simple diagram.
|
182 | If you drag one of the nodes, you will see that the link remains connected to it.
|
183 | </p>
|
184 | <p>
|
185 | Although this way of building a diagram will work, it will not scale up well when creating large diagrams.
|
186 | Normally you will want a varying number of nodes each of which is very similar to the others.
|
187 | It would be better to share the construction of the node but parameterize a few things where the values should vary.
|
188 | </p>
|
189 | <p>
|
190 | One possibility would be put the code to build a Node into a function that returned a fully constructed Node,
|
191 | including all of the Panels and other GraphObjects in its visual tree.
|
192 | You would probably want to parameterize the function in order to provide the desired strings and colors and figures and image URLs.
|
193 | However such an approach is very ad-hoc: it would be difficult for the system to know how to automatically call such functions
|
194 | in order to create new nodes or new links on demand.
|
195 | Furthermore as your application data changes dynamically, how would you use such functions to update properties
|
196 | of existing objects within existing nodes and links, without inefficiently re-creating everything?
|
197 | And if you wanted anything/everything to update automatically as your application data changes,
|
198 | how would the system know what to do?
|
199 | </p>
|
200 | <p>
|
201 | This diagram-building code is also more cumbersome than it needs to be
|
202 | to manage references to nodes so that you can link them up.
|
203 | This is similar to the earlier problem when building a node's visual tree in code
|
204 | of having to use temporary named variables and referring to them when needed.
|
205 | </p>
|
206 | <p>
|
207 | What we are looking for is the separation of the appearance, definition, and construction
|
208 | of 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>
|
213 | One way of achieving the separation of node appearance from node data is to use a data model and node templates.
|
214 | A model is basically just a collection of data that holds the essential information for each node and each link.
|
215 | A 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>
|
218 | In fact, a <a>Diagram</a> already has very simple default templates for Nodes and Links.
|
219 | If you want to customize the appearance of the nodes in your diagram,
|
220 | you can replace the default node template by setting <a>Diagram.nodeTemplate</a>.
|
221 | </p>
|
222 | <p>
|
223 | To automatically make use of templates, provide the diagram a model holding the data for each node and the data for each link.
|
224 | A <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>.
|
226 | You then set the <a>Diagram.model</a> property so that the diagram can create <a>Node</a>s for all of the node data
|
227 | and <a>Link</a>s for all of the link data.
|
228 | </p>
|
229 | <p>
|
230 | Models interpret and maintain references between the data.
|
231 | Each node data is expected to have a unique key value so that references to node data can be resolved reliably.
|
232 | Models also manage dynamically adding and removing data.
|
233 | </p>
|
234 | <p>
|
235 | The node data and the link data in models can be any JavaScript object.
|
236 | You get to decide what properties those objects have -- add as many as you need for your app.
|
237 | Since this is JavaScript, you can even add properties dynamically.
|
238 | There are several properties that <b>GoJS</b> models assume exist on the data,
|
239 | such as "key" (on node data) and "category" and "from" and "to" (the latter two on link data).
|
240 | However you can tell the model to use different property names by setting the model
|
241 | properties whose names end in "...Property".
|
242 | </p>
|
243 | <p>
|
244 | A node data object normally has its node's unique key value in the "key" property.
|
245 | Currently node data keys must be strings or numbers.
|
246 | You 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>
|
249 | Let us create a diagram providing the minimal amount of necessary information.
|
250 | The particular node data has been put into an array of JavaScript objects.
|
251 | We declare the link relationships in a separate array of link data objects.
|
252 | Each link data holds references to the node data by using their keys.
|
253 | Normally 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>
|
267 | This results in two nodes and a link, but the nodes do not appear the way we want.
|
268 | So 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>
|
292 | Now the graph looks better, but the nodes have not been parameterized -- they are all identical!
|
293 | We can achieve that parameterization by using data binding.
|
294 | </p>
|
295 |
|
296 | <h2 id="ParameterizingNodesUsingDataBindings">Parameterizing Nodes using data binding</h2>
|
297 | <p>
|
298 | A data binding is a declarative statement that the value of the property of one object
|
299 | should be used to set the value of a property of another object.
|
300 | </p>
|
301 | <p>
|
302 | In this case, we want to make sure that the <a>TextBlock.text</a> property gets the
|
303 | "key" value of the corresponding node data.
|
304 | And we want to make sure that the <a>Shape.fill</a> property gets set to the color/brush given
|
305 | by the "color" property value of the corresponding node data.
|
306 | </p>
|
307 | <p>
|
308 | We can declare such data-bindings by creating <a>Binding</a> objects and associating them with the target <a>GraphObject</a>.
|
309 | Programmatically you do this by calling <a>GraphObject.bind</a>.
|
310 | But 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>
|
335 | Now we have the same diagram result as before, but it is implemented in much more general manner.
|
336 | You can easily add more node and link data to build bigger diagrams.
|
337 | And you can easily change the appearance of all of the nodes without modifying the data.
|
338 | </p>
|
339 | <p>
|
340 | Actually, you may notice that the <a>Link</a> is different: it has an arrowhead.
|
341 | No arrowhead was included when we first built this diagram using code.
|
342 | But the default <a>Diagram.linkTemplate</a> includes an arrowhead
|
343 | and we did not replace the link template with a custom one in this example.
|
344 | </p>
|
345 | <p>
|
346 | Notice that the value of <a>Shape.fill</a> in the template above gets a value twice.
|
347 | First it is set to "white". Then the binding sets it to whatever value the node data's "color" property has.
|
348 | It may be useful to be able to specify an initial value that remains in case the node data does
|
349 | not have a "color" property or if there is an error getting that value.
|
350 | </p>
|
351 | <p>
|
352 | At this point we can also be a bit more precise about what a template is.
|
353 | A template is a <a>Part</a> that may have some data <a>Binding</a>s and that is not itself in a diagram
|
354 | but may be copied to create parts that are added to a diagram.
|
355 | </p>
|
356 |
|
357 | <h3 id="TemplateDefinitions">Template Definitions</h3>
|
358 | <p>
|
359 | The implementations of all predefined templates are provided in <a href="../extensions/Templates.js">Templates.js</a> in the Extensions directory.
|
360 | You may wish to copy and adapt these definitions when creating your own templates.
|
361 | </p>
|
362 | <p>
|
363 | Those definitions might not be an up-to-date description
|
364 | of the actual standard template implementations that are in <b>GoJS</b>.
|
365 | </p>
|
366 |
|
367 | <h2 id="KindsOfModels">Kinds of Models</h2>
|
368 | <p>
|
369 | A model is a way of interpreting a collection of data objects as an abstract graph
|
370 | with various kinds of relationships determined by data properties and the assumptions that the model makes.
|
371 | The simplest kind of model, <a>Model</a>, can only hold "parts" without any relationships between them --
|
372 | no 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>
|
376 | The kind of model you have seen above, <a>GraphLinksModel</a>, is actually the most general kind.
|
377 | It supports link relationships using a separate link data object for each <a>Link</a>.
|
378 | There is no inherent limitation on which <a>Nodes</a> a Link may connect, so reflexive and duplicate links are allowed.
|
379 | Links might also result in cycles in the graph.
|
380 | However you may prevent the user from drawing such links by setting various properties, such as <a>Diagram.validCycle</a>.
|
381 | And if you want to have a link appear to connect with a link rather than with a node,
|
382 | this is possible by having special nodes, known as "label nodes", that belong to links and are arranged along the
|
383 | path of a link in the same manner as text labels are arranged on a link.
|
384 | </p>
|
385 | <p>
|
386 | Furthermore a <a>GraphLinksModel</a> also supports identifying logically and physically different connection objects,
|
387 | known as "ports", within a <a>Node</a>.
|
388 | Thus an individual link may connect with a particular port rather than with the node as a whole.
|
389 | The <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>
|
392 | A <a>GraphLinksModel</a> also supports the group-membership relationship.
|
393 | Any <a>Part</a> can belong to at most one <a>Group</a>; no group can be contained in itself, directly or indirectly.
|
394 | You 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>
|
398 | A simpler kind of model, the <a>TreeModel</a>, only supports link relationships that form a tree-structured graph.
|
399 | There is no separate link data, so there is no "linkDataArray".
|
400 | The 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.
|
401 | If that property, whose name defaults to "parent", is undefined, then that data's corresponding node is a tree root.
|
402 | Each <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>
|
425 | Many of the tree-oriented samples make use of a TreeModel instead of a GraphLinksModel.
|
426 | But just because your graph is tree-structured does not mean you have to use a TreeModel.
|
427 | You may find that your data is organized with a separate "table" defining the link relationships,
|
428 | so that using a GraphLinksModel is most natural.
|
429 | Or you may want to use other features that TreeModel does not support.
|
430 | </p>
|
431 | <p>
|
432 | Other 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>
|
508 | If 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.
|
510 | Use the <a>Model.findNodeDataForKey</a> method to find a particular node data object if you only have its unique key value.
|
511 | You 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>
|
514 | It does not work to simply mutate the Array that is the value of <a>Model.nodeDataArray</a>,
|
515 | because the <b>GoJS</b> software will not be notified about any change to any JavaScript Array and
|
516 | thus 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>
|
520 | Similarly, it does not work to simply set a property of a node data object.
|
521 | Any <a>Binding</a> that depends on the property will not be notified about any changes,
|
522 | so it will not be able to update its target <a>GraphObject</a> property.
|
523 | For 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>
|
531 | Instead 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>
|
539 | Calling model methods such as <a>Model.addNodeData</a> or <a>Model.setDataProperty</a> is required
|
540 | when the JavaScript Array or Object is already part of the Model.
|
541 | When first building the Array of Objects for the <a>Model.nodeDataArray</a>
|
542 | or when initializing a JavaScript Object as a new node data object, such calls are not necessary.
|
543 | But 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.
|
591 | But because this is JavaScript and JSON is the most popular data-interchange format,
|
592 | we do make it easy to write and read models as text in JSON format.
|
593 | </p>
|
594 | <p>
|
595 | Just call <a>Model.toJson</a> to generate a string representing your model.
|
596 | Call the static method <a>Model,fromJson</a> to construct and initialize a model given a string produced by <a>Model.toJson</a>.
|
597 | Many of the samples demonstrate this -- search for JavaScript functions named "save" and "load".
|
598 | Most 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.
|
599 | But 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>
|
602 | JSON formatted text has strict limits on the kinds of data that you can represent without additional assumptions.
|
603 | To 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.
|
614 | However 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>
|
617 | Because you are using JavaScript, it is trivial for you to add data properties to your node data.
|
618 | This allows you to associate whatever information you need with each node.
|
619 | But if you need to associate some information with the model, which will be present even if there is no node data at all,
|
620 | you can add properties to the <a>Model.modelData</a> object.
|
621 | This 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 |
|
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>
|