UNPKG

37.9 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 and Angular -- 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<div id="container" class="container-fluid">
133 <div id="content">
134
135 <h1>Using GoJS with Angular</h1>
136 <p class="box" style="background-color: lightgoldenrodyellow;">
137 Examples of most of the topics discussed on this page can be found in the <a href="https://github.com/NorthwoodsSoftware/gojs-angular-basic"
138 target="_blank">gojs-angular-basic</a>
139 project,
140 which serves as a simple starter project.
141 </p>
142
143 <p>
144 If you are new to GoJS, it may be helpful to first visit the <a href="../learn/index.html" target="_blank">
145 Getting
146 Started Tutorial
147 </a>.
148 </p>
149
150 <p>
151 The easiest way to get a component set up for a GoJS Diagram is to use the <a href="#TODO" target="_blank">gojs-angular</a>
152 package,
153 which exports Angular Components for GoJS Diagrams, Palettes, and Overviews.
154 More information about the package, including the various props it takes, can be found on the
155 <a href="https://npmjs.com/gojs-react" target="_blank">NPM</a> page. Examples here will be using a <a>GraphLinksModel</a>,
156 but any model can be used.
157 </p>
158
159 <p>
160 <b>Note:</b> This page is assumes use of <code>gojs-angular</code> version 2.0+. <a href="#MigratingTo2.0">Click here</a> for a discussion
161 on upgrading to version 2.0.
162 </p>
163
164 <h2 id="GeneralInformation">General Information</h2>
165 <h3 id="Installation">Installation</h3>
166 <p>
167 To use the published components, make sure you install GoJS and gojs-angular: <code>npm install gojs gojs-angular</code>.
168 </p>
169
170 <h3 id="AboutComponentStyling">About Component Styling</h3>
171 <p>
172 Whether you are using a Diagram, Palette, or Overview <code>gojs-angular</code> component, you will
173 probably
174 want to style them.
175 First, you'll need to style a CSS class for the div of your GoJS Diagram / Palette / Overview such as:
176 </p>
177 <pre class="lang-css"> <code>
178/* app.component.css */
179.myDiagramDiv {
180 background: whitesmoke;
181 width: 800px;
182 height: 300px;
183 border: 1px solid black;
184}
185 </code> </pre>
186
187 <p>
188 To style the GoJS Diagram / Palette / Overivew div, which will reside in the <code>gojs-angular</code>
189 component(s) you are using, make sure you set the <code>@Component</code> decorator's <code>encapsulation</code>
190 propety to either
191 <code>ViewEncapsulation.None</code> or <code>ViewEncapsulation.ShadowDown</code>. Without this, your
192 styling will not effect
193 the
194 component divs.
195 Read more about Angular view encapsulation <a href="https://angular.io/api/core/ViewEncapsulation">here</a>.
196 </p>
197 <p>
198 Your <code>@Component</code> decorator for the component holding the your GoJS / Angular Component(s)
199 should
200 look something
201 like:
202 </p>
203 <pre class="lang-ts"> <code>
204@Component({
205 selector: 'app-root',
206 templateUrl: './app.component.html',
207 styleUrls: ['./app.component.css'],
208 encapsulation: ViewEncapsulation.None
209})
210</code></pre>
211 <p>
212 <strong>Note:</strong> You may alternatively use the default <code>ViewEncapsulation.Emulated</code>
213 value, if you assign additional CSS modifiers :host and ::ng-deep to your class selector. Be warned,
214 however, ng-deep is technically deprecated, so this is not best practice.
215 </p>
216
217 <h2 id="DataSyncService">The DataSyncService</h2>
218
219 <p>
220 The <code>gojs-angular</code> package comes with an Angular <a href="https://angular.io/tutorial/toh-pt4">service</a>
221 called DataSyncService, used to
222 easily merge changes (a <a href="https://gojs.net/latest/api/symbols/IncrementalData.html">go.IncrementalData</a>
223 instance) with an Array of Node or Link Data, or a <a href="https://gojs.net/latest/api/symbols/Model.html#modelData">
224 modelData
225 </a> object.
226 </p>
227 <p>This service has three static functions: </p>
228 <ul>
229 <li>
230 <code>syncNodeData(changes, array)</code> - Merges any node data changes in a go.IncrementalData
231 object
232 with a given array of node data, then returns the new array
233 </li>
234 <li>
235 <code>syncLinkData(changes, array)</code> - Merges any link data changes in a go.IncrementalData
236 object
237 with a given array of link data, then returns the new array. <strong>Note</strong>: Ensure you set
238 the <a href="https://gojs.net/latest/api/symbols/GraphLinksModel.html#linkKeyProperty">linkKeyProperty</a>
239 if you are using GraphLinksModel, so data merging is possible.
240 </li>
241 <li>
242 <code>syncModelData(changes, object)</code> - Merges any modelData changes in a go.IncrementalData
243 object
244 with a given modelData object, then returns the new object
245 </li>
246 </ul>
247
248 <p>
249 These functions should allow you to keep your data synced up as needed, without needing to write lots
250 of
251 code.
252 </p>
253
254 <h3 id="ListeningForModelChanges">Listening for Model Changes</h3>
255 <p>
256 It is common to listen for data changes in a Diagram or Palette, then do
257 something with
258 those changes on an application-level (such as syncing those changes with app-level data). That's why,
259 for both the DiagramComponent
260 and
261 PaletteComponent, there is a <code>modelChange</code> <code>@Input</code> property function. This is a
262 prime example of where the DataSyncService can be used.
263 </p>
264
265 <p>
266 As of <code>gojs-angular</code> 2.0, Array / object <code>@Input</code>
267 properties are assumed to be immutable. As such, when updating these properties, new Arrays / objects
268 must be generated -- one cannot simply mutate an element of the Array or object.
269 Please see the <a href="https://github.com/NorthwoodsSoftware/gojs-angular-basic">gojs-angular-basic</a>
270 project for examples of both maintaining state immutability and usage of the DataSyncService.
271 </p>
272
273 <p>
274 <strong>Note:</strong> The <a href="https://gojs.net/latest/api/symbols/UndoManager.html">UndoManager</a>
275 should always be
276 enabled to allow for transactions to take place, but its
277 <a href="https://gojs.net/latest/api/symbols/UndoManager.html#maxHistoryLength">maxHistoryLength</a>
278 can be set to 0 to prevent undo and redo.
279 </p>
280
281 <br>
282 <h2 id="UsingDiagramComponent">The Diagram and Palette Components</h2>
283 <p>The Diagram and Palette Components accept a similar set of <code>@Input</code> properties.</p>
284 <p>
285 Diagram Component accepts:
286 <ul>
287 <li>
288 <code>initDiagram</code> - A function that must return a GoJS Diagram. You may define your
289 Diagram's
290 Node
291 and Link templates here.
292 </li>
293 <li><code>divClassName</code> - A class name for your Diagram div</li>
294 <li><code>nodeDataArray</code> - An array containing data objects for your nodes </li>
295 <li><code>linkDataArray</code> - An array containing data objects for your links. Optional. </li>
296 <li>
297 <code>modelData</code> - A data object,
298 containing your diagram's <a href="https://gojs.net/latest/api/symbols/Model.html#modelData">model.modelData</a>.
299 Optional.
300 <li>
301 <code>skipsDiagramUpdate</code> - A boolean flag, specifying whether the component should skip
302 updating,
303 often set when updating state from a GoJS model change.
304 </li>
305 <li>
306 <code>modelChange</code> - A function, which accepts a <a href="">go.IncrementalData</a>
307 object.
308 This function will fire when your Diagram's model changes, allowing you to decide what to do
309 with those
310 changes. A common practice is to sync your app-level data to reflect the changes in the diagram
311 model,
312 which
313 is made simple using the DataSyncService <code>gojs-angular</code> ships with.
314 </li>
315 </ul>
316 </p>
317
318 <p>
319 The Palette Component accepts:
320 </p>
321 <ul>
322 <li>
323 <code>initPalette</code> - A function that must return a GoJS Palette. You may define your
324 Palette's Node
325 and Link templates here.
326 </li>
327 <li><code>divClassName</code> - A class name for the div your Palette div</li>
328 <li><code>nodeDataArray</code> - An array containing data objects for your nodes </li>
329 <li><code>linkDataArray</code> - An array containing data objects for your links. Optional. </li>
330 <li>
331 <code>modelData</code> - A data object,
332 containing your palette's <a href="https://gojs.net/latest/api/symbols/Model.html#modelData">model.modelData</a>.
333 Optional.
334 </ul>
335 <p>
336 Because GoJS Palettes are read-only by default, there is no <code>modelChange</code> property in PaletteComponent. Since there won't be user-driven changes to a Palette's model,
337 changes to node/link/model data should be achieved by immutably altering the analogous above @Input properties.
338 </p>
339
340 <h2>Sample Diagram / Palette Component Usage</h2>
341 <p>
342 Here is an example of how one might set up their Diagram / Palette component properties
343 <pre class="lang-ts"> <code>
344// Big object that holds app-level state data
345// As of gojs-angular 2.0, immutability is required of state for change detection
346public state = {
347 // Diagram state props
348 diagramNodeData: [
349 { id: 'Alpha', text: "Alpha", color: 'lightblue' },
350 { id: 'Beta', text: "Beta", color: 'orange' }
351 ],
352 diagramLinkData: [
353 { key: -1, from: 'Alpha', to: 'Beta' }
354 ],
355 diagramModelData: { prop: 'value' },
356 skipsDiagramUpdate: false,
357
358 // Palette state props
359 paletteNodeData: [
360 { key: 'PaletteNode1', color: 'firebrick' },
361 { key: 'PaletteNode2', color: 'blueviolet' }
362 ]
363}; // end state object
364
365public diagramDivClassName: string = 'myDiagramDiv';
366public paletteDivClassName = 'myPaletteDiv';
367
368// initialize diagram / templates
369public initDiagram(): go.Diagram {
370 const $ = go.GraphObject.make;
371 const dia = $(go.Diagram, {
372 'undoManager.isEnabled': true,
373 model: $(go.GraphLinksModel,
374 {
375 nodeKeyProperty: 'id',
376 linkKeyProperty: 'key' // IMPORTANT! must be defined for merges and data sync when using GraphLinksModel
377 }
378 )
379 });
380
381 // define the Node template
382 dia.nodeTemplate =
383 $(go.Node, 'Auto',
384 $(go.Shape, 'RoundedRectangle', { stroke: null },
385 new go.Binding('fill', 'color')
386 ),
387 $(go.TextBlock, { margin: 8, editable: true },
388 new go.Binding('text').makeTwoWay())
389 );
390 return dia;
391}
392
393/**
394 * Handle GoJS model changes, which output an object of data changes via Mode.toIncrementalData.
395 * This method should iterate over thoe changes and update state to keep in sync with the FoJS model.
396 * This can be done with any preferred state management method, as long as immutability is preserved.
397 */
398public diagramModelChange = function(changes: go.IncrementalData) {
399 console.log(changes);
400 // see gojs-angular-basic for an example model changed handler that preserves immutability
401 // when setting state, be sure to set skipsDiagramUpdate: true since GoJS already has this update
402};
403
404public initPalette(): go.Palette {
405 const $ = go.GraphObject.make;
406 const palette = $(go.Palette);
407
408 // define the Node template
409 palette.nodeTemplate =
410 $(go.Node, 'Auto',
411 $(go.Shape, 'RoundedRectangle', { stroke: null },
412 new go.Binding('fill', 'color')
413 ),
414 $(go.TextBlock, { margin: 8 },
415 new go.Binding('text', 'key'))
416 );
417
418 palette.model = $(go.GraphLinksModel,
419 {
420 linkKeyProperty: 'key' // IMPORTANT! must be defined for merges and data sync when using GraphLinksModel
421 });
422
423 return palette;
424}
425</code></pre>
426
427 <p>
428 Once you've defined your <code>@Input</code> properties for your components, pass these properties
429 to your DiagramComponent and PaletteComponent in your template, like so:
430 </p>
431
432 <pre class="lang-html"> <code>
433&lt;gojs-diagram
434 [initDiagram]='initDiagram'
435 [nodeDataArray]='state.diagramNodeData'
436 [linkDataArray]='state.diagramLinkData'
437 [modelData]='state.diagramModelData'
438 [skipsDiagramUpdate]='state.skipsDiagramUpdate'
439 (modelChange)='diagramModelChange($event)'
440 [divClassName]='diagramDivClassName'&gt;
441&lt;/gojs-diagram&gt;
442
443&lt;gojs-palette
444 [initPalette]='initPalette'
445 [nodeDataArray]='state.paletteNodeData'
446 [divClassName]='paletteDivClassName'&gt;
447&lt;/gojs-palette&gt;
448</code></pre>
449
450 <p>
451 You will now have a GoJS Diagram and Palette working in your Angular application. Again, for a full
452 example of a <code>gojs-angular</code> application, see <a href="https://github.com/NorthwoodsSoftware/gojs-angular-basic">gojs-angular-basic</a>.
453 </p>
454
455 <h4>A Note on Diagram Reinitialization</h4>
456 <p>
457 Occasionally you may want to treat a model update as if you were loading a completely new model.
458 But initialization is only done in your <code>initDiagram</code> function,
459 within the <code>DiagramComponent</code>'s <code>ngAfterViewInit</code> lifecycle hook, and only once.
460 A regular model update is not treated as an initialization, so none of the <code>initial...</code> properties
461 of your Diagram will apply.
462 </p>
463 <p>
464 To address this problem, <code>DiagramComponent</code> exposes a <code>clear</code> method.
465 When called, it clears its diagram of all nodes, links, and model data, and
466 prepares the next state update to be treated as a diagram initialization.
467 That will result in an initial layout and perform initial diagram content alignment and scaling.
468 Note that <code>initDiagram</code> is not called again.
469 </p>
470 <p>
471 Here is a small sample of how one might trigger diagram reinitilization using the <code>clear</code> method with <code>gojs-angular</code> 2.0.
472 </p>
473<pre class="lang-ts"><code>
474public reinitModel() {
475 this.myDiagramComponent.clear();
476 this.state = produce(this.state, draft => {
477 draft.skipsDiagramUpdate = false;
478 draft.diagramNodeData = [{ id: "Alpha", text: "Zorro", color: "red" }];
479 draft.diagramLinkData = [];
480 });
481}
482</code></pre>
483
484 <br>
485 <h2 id="UsingOverviewComponent">Using the Overview Component</h2>
486 <p>
487 The Overview Component accepts the following Angular <code>@Input()</code> properties.
488 </p>
489 <ul>
490 <li><code>initOverview</code> - A function that must return a GoJS Overview.</li>
491 <li><code>divClassName</code> - A class name for your Overview div</li>
492 <li><code>observedDiagram</code> - The GoJS Diagram this Overview observes</li>
493 </ul>
494
495 <p>
496 Define these properties in the component that will hold your Overview Component, like:
497 </p>
498
499 <pre class="lang-ts"> <code>
500public oDivClassName = 'myOverviewDiv';
501public initOverview(): go.Overview {
502 const $ = go.GraphObject.make;
503 const overview = $(go.Overview);
504 return overview;
505}
506public observedDiagram = null;</code></pre>
507
508 <p>
509 Then pass these properties to your Overview Component in your template, like:
510 </p>
511
512 <pre class="lang-html"> <code>
513&lt;gojs-overview
514 [initOverview]='initOverview'
515 [divClassName]='oDivClassName'
516 [observedDiagram]='observedDiagram'&gt;
517&lt;/gojs-overview&gt; </code></pre>
518
519 <p>
520 But, we're not done yet. <code>observedDiagram</code> is null, so the Overview won't observe
521 anything.
522 To assign your Overview a Diagram to observe, you will have to reassign the <code>observedDiagram</code>
523 property after initialization. To do so,
524 reassign the bound <code>observedDiagram</code> property in your component holding your Overview
525 Component in the <code>ngAfterViewInit</code> lifecycle hook.
526 </p>
527 <p>
528 <strong>Note</strong>: To avoid a <code>ExpressionChangedAfterItHasBeenCheckedError</code>, you
529 must
530 inform
531 Angular
532 to then detect changes.
533 This can be done with the <a href="https://angular.io/api/core/ChangeDetectorRef#detectchanges">ChangeDetectorRef.detectChanges()</a>
534 method. You can inject a ChangeDetectorRef instance
535 into your wrapper Component constructor, and use that after you alter <code>observedDiagram</code>
536 to
537 call
538 detectChanges(). Like so:
539 </p>
540
541 <pre class="lang-ts"> <code>
542constructor(private cdr: ChangeDetectorRef) { }
543
544public ngAfterViewInit() {
545 if (this.observedDiagram) return;
546 // in this snippet, this.myDiagramComponent is a reference to a GoJS/Angular Diagram Component
547 // that has a valid GoJS Diagram
548 this.observedDiagram = this.myDiagramComponent.diagram;
549
550 // IMPORTANT: without this, Angular will throw ExpressionChangedAfterItHasBeenCheckedError (dev mode only)
551 this.cdr.detectChanges();
552}
553 </code></pre>
554
555 <p>
556 Now, after initialization, your Overview should display appropriately.
557 </p>
558
559 <br>
560 <h2 id="UpdatingPropertiesBasedOnAppState">Updating Properties Based on App State</h2>
561 <p>
562 You may have some app-level properties you want to affect the behavior / appearance of your
563 Diagram,
564 Palette,
565 or Overview. You could subclass their respective components and add <code>@Input</code> bindings
566 with
567 specific
568 setter methods, or, more simply, you can have an <code>ngOnChanges</code> function in your
569 app-level
570 component
571 that updates various Diagram / Palette / Component properties based on your app state.
572 </p>
573
574 <p>
575 For example, say you have an app-level property called <code>showGrid</code>. When <code>showGrid</code>
576 is
577 true, your Diagram's grid should be visible -- when false, it should be invisible. In your
578 AppComponent, you
579 could do something like:
580 </p>
581
582 <pre class="lang-ts"> <code>
583
584// myDiagramComponent is a reference to your DiagramComponent
585@ViewChild('myDiagram', { static: true }) public myDiagramComponent: DiagramComponent;
586
587public ngOnChanges () {
588 // whenever showGrid changes, update the diagram.grid.visible in the child DiagramComponent
589 if (this.myDiagramComponent && this.myDiagramComponent.diagram instanceof go.Diagram) {
590 this.myDiagramComponent.diagram.grid.visible = this.showGrid;
591 }
592}
593 </code></pre>
594
595 <h2 id="MigratingTo2.0">Migrating to 2.0</h2>
596 <p>
597 This page assumes use of <code>gojs-angular</code> version 2.0, which
598 requires
599 immutable state, unlike version 1.0. It is recommended to use the 2.0 version. If you have a
600 <code>gojs-angular</code> project using version 1.x and want to upgrade, reference this section for tips on migrating to version 2.
601 </p>
602 <h3>Should I Upgrade?</h3>
603 <p>
604 In general, yes.
605 </p>
606 <p>
607 If you have very simple node and link data, using the latest 1.x version might be okay. But new features and quality of life changes
608 will be published on the
609 2.x branch moving forward.
610 </p>
611 <p>
612 Version 2.0 handles complex nested data much better than the previous version, due to its focus on immutable data. Additionally, it is a bit
613 smaller in file size.
614 </p>
615 <p>
616 One may wish to hold off on upgrading if they have lots of operations mutating their <code>@Input</code> properties, and they do not want to take the
617 time to rewrite those operations immutably. However, the guide below details one way one could do this. Our <code>gojs-angular-basic</code> sample also
618 has demonstrations of immutably updating <code>@Input</code> properties to make such a rewrite easier.
619 </p>
620 <h3>Upgrade gojs-angular Version </h3>
621 <p>
622 Update your <code>package.json</code> to require <code>gojs-angular</code> version 2.0 or greater,
623 then run <code>npm install</code>
624 </p>
625 <p>
626 It is also recommended to upgrade to the lastest version of <code>gojs</code>.
627 </p>
628 <h3>Immutability</h3>
629 <p>
630 The biggest change with 2.0 is you must enforce immutability of <code>@Input</code>
631 properties to your Diagram and Palette components.
632 </p>
633 <p>
634 So, for instance, whenever an entry of <code>diagramNodeData</code>
635 is updated, removed, or changed, you will need to generate a whole new Array for <code>DiagramComponent.diagramNodeData</code>. This can be done in
636 many different ways with many different packages. A popular choice is <a href="https://github.com/immerjs/immer">immer</a>, which exposes a <code>produce</code> function
637 that allows one to immutability manipulate their data on a <code>draft</code> variable. We will use that function here for demonstration purposes.
638 </p>
639
640 <h4>The Version 1.0 Way</h4>
641 <p>
642 In <code>gojs-angular</code> version 1, if you wanted to add some node data to your <code>diagramNodeData</code> <code>@Input</code> property,
643 you could do so by simply adding to the <code>diagramNodeData</code>, mutating it. Such as:
644 </p>
645
646 <pre class="lang-ts"><code>
647public addNode = function(nodeData: go.ObjectData) {
648 // sync changes with GoJS model
649 this.skipsDiagramUpdate = false;
650 this.diagramNodeData.push(nodeData);
651};
652 </code></pre>
653
654 <h4>The Version 2.0 Way</h4>
655 <p>
656 In <code>gojs-angular</code> version 2, that same <code>addNode</code> function must be changed so the <code>diagramNodeData</code>
657 property is updated immutably (that is, replaced with an entirely new Array). Here is an example of
658 doing that with immer's <code>produce</code> function.
659 </p>
660
661 <pre class="lang-ts"><code>
662public addNode = function(nodeData: go.ObjectData) {
663 this.state = produce(this.state, draft => {
664 var nodedata = { id: "Zeta", text: "Zorro", color: "red" };
665 draft.skipsDiagramUpdate = false;
666 draft.diagramNodeData.push(nodedata);
667 });
668}
669 </code></pre>
670
671 <p>
672 Notice we are also using a massive <code>state</code> object to hold <code>gojs-angular</code> component properties. This makes these kinds
673 of immutable operations (especially if you are using immer, or a package like it) straightforward (see how we were able to update both <code>skipsDiagramUpdate</code>
674 and <code>diagramNodeData</code> on the same <code>draft</code> variable).
675 </p>
676
677 <p>
678 To see more samples of enforcing immutability with <code>gojs-angular</code>, see <a href="https://github.com/NorthwoodsSoftware/gojs-angular-basic">gojs-angular-basic</a>,
679 particularly the <code>modelChange</code> property of the Diagram Component.
680 </p>
681 <h3>Additional Considerations</h3>
682 <p>
683 Additionally, PaletteComponent no longer supports <code>skipsPaletteUpdate</code> or <code>modelChange</code> properties. As GoJS Palettes are read-only by default, their models should not
684 be changing based on user input. Instead, if you need to update their node/link/model data, update their <code>@Input</code> properties (immutably, of course).
685 </p>
686
687
688 </div>
689</div>
690 </div>
691 </div>
692
693 <div class="bg-nwoods-primary">
694 <section class="max-w-screen-lg text-white container mx-auto py-2 px-12">
695 <p id="version" class="leading-none mb-2 my-4">GoJS</p>
696 </section>
697 </div><footer class="bg-nwoods-primary text-white">
698 <div class="container max-w-screen-lg mx-auto px-8">
699 <div class="w-full py-6">
700
701 <div class="max-w-screen-lg xl:max-w-screen-xl mx-auto px-4 sm:px-6 md:px-8">
702 <ul class="text-sm font-medium pb-14 sm:pb-20 grid grid-cols-1 sm:grid-cols-3 gap-y-10">
703 <li class="list-none row-span-2">
704 <h2 class="text-base font-semibold tracking-wide">GoJS</h2>
705 <ul class="list-none space-y-4 md:space-y-1 px-0">
706 <li>
707 <a href="../samples/index.html">Samples</a>
708 </li>
709 <li>
710 <a href="../learn/index.html">Learn</a>
711 </li>
712 <li>
713 <a href="../intro/index.html">Intro</a>
714 </li>
715 <li>
716 <a href="../api/index.html">API</a>
717 </li>
718 <li>
719 <a href="../changelog.html">Changelog</a>
720 </li>
721 <li>
722 <a href="https://github.com/NorthwoodsSoftware/GoJS">GitHub</a>
723 </li>
724 </ul>
725 </li>
726 <li class="list-none row-span-2">
727 <h2 class="text-base font-semibold tracking-wide">Support</h2>
728 <ul class="list-none space-y-4 md:space-y-1 px-0">
729 <li>
730 <a href="https://www.nwoods.com/contact.html"
731 target="_blank" rel="noopener" onclick="getOutboundLink('https://www.nwoods.com/contact.html', 'contact');">Contact</a>
732 </li>
733 <li>
734 <a href="https://forum.nwoods.com/c/gojs">Forum</a>
735 </li>
736 <li>
737 <a href="https://www.nwoods.com/app/activate.aspx?sku=gojs">Activate</a>
738 </li>
739 <li>
740 <a href="https://www.nwoods.com/sales/index.html"
741 target="_blank" rel="noopener" onclick="getOutboundLink('https://www.nwoods.com/sales/index.html', 'buy');">Buy</a>
742 </li>
743 <li>
744 <a href="https://www.youtube.com/channel/UC9We8EoX596-6XFjJDtZIDg">Videos</a>
745 </li>
746 </ul>
747 </li>
748 <li class="list-none row-span-2">
749 <h2 class="text-base font-semibold tracking-wide">Company</h2>
750 <ul class="list-none space-y-4 md:space-y-1 px-0">
751 <li>
752 <a href="https://www.nwoods.com">Northwoods</a>
753 </li>
754 <li>
755 <a href="https://www.nwoods.com/about.html">About Us</a>
756 </li>
757 <li>
758 <a href="https://www.nwoods.com/contact.html">Contact Us</a>
759 </li>
760 <li>
761 <a href="https://twitter.com/northwoodsgo">Twitter</a>
762 </li>
763
764 </ul>
765 </li>
766 </ul>
767
768
769 <p class="text-sm text-gray-100 md:mb-6">
770 Copyright 1998-2021 <a class="text-white" href="https://www.nwoods.com">Northwoods Software</a>
771 </p>
772 </div>
773 </div>
774</footer> </body>
775
776<script async src="https://www.googletagmanager.com/gtag/js?id=UA-1506307-5"></script>
777<script>
778 window.dataLayer = window.dataLayer || [];
779 function gtag(){dataLayer.push(arguments);}
780 gtag('js', new Date()); gtag('config', 'UA-1506307-5');
781 var getOutboundLink = function(url, label) {
782 gtag('event', 'click', {
783 'event_category': 'outbound',
784 'event_label': label,
785 'transport_type': 'beacon'
786 });
787 }
788
789 // topnav
790 var topButton = document.getElementById("topnavButton");
791 var topnavList = document.getElementById("topnavList");
792 topButton.addEventListener("click", function() {
793 this.classList.toggle("active");
794 topnavList.classList.toggle("hidden");
795 document.getElementById("topnavOpen").classList.toggle("hidden");
796 document.getElementById("topnavClosed").classList.toggle("hidden");
797 });
798</script>
799 <script src="../assets/js/prism.js"></script>
800 <script src="../release/go.js"></script>
801 <script src="../assets/js/goDoc.js"></script>
802 <script>
803 document.addEventListener("DOMContentLoaded", function() {
804 if (window.go) document.getElementById('version').textContent = "GoJS version " + go.version;
805 if (window.goDoc) window.goDoc();
806 var d = window.diagrams;
807 for (var i = 0; i < d.length; i++) {
808 var dargs = d[i];
809 goCodeExecute(dargs[0], dargs[1], dargs[2], dargs[3], dargs[4]);
810 }
811 if (window.extra) window.extra();
812 });
813 </script>
814</html>