Using GoJS with Angular
Examples of most of the topics discussed on this page can be found in the gojs-angular-basic project, which serves as a simple starter project.
If you are new to GoJS, it may be helpful to first visit the Getting Started Tutorial .
The easiest way to get a component set up for a GoJS Diagram is to use the gojs-angular package, which exports Angular Components for GoJS Diagrams, Palettes, and Overviews. More information about the package, including the various props it takes, can be found on the NPM page. Examples here will be using a GraphLinksModel, but any model can be used.
      Note: This page is assumes use of gojs-angular version 2.0+. Click
        here for a discussion
      on upgrading to version 2.0.
    
General Information
Installation
      To use the published components, make sure you install GoJS and gojs-angular:
      npm install gojs gojs-angular.
    
About Component Styling
      Whether you are using a Diagram, Palette, or Overview gojs-angular component, you will
      probably
      want to style them.
      First, you'll need to style a CSS class for the div of your GoJS Diagram / Palette / Overview such as:
    
 
/* app.component.css */
.myDiagramDiv {
  background: whitesmoke;
  width: 800px;
  height: 300px;
  border: 1px solid black;
}
     
    
      To style the GoJS Diagram / Palette / Overview div, which will reside in the gojs-angular
      component(s) you are using, make sure you set the @Component decorator's encapsulation
      property to either
      ViewEncapsulation.None or ViewEncapsulation.ShadowDown. Without this, your
      styling will not effect
      the
      component divs.
      Read more about Angular view encapsulation here.
    
      Your @Component decorator for the component holding the your GoJS / Angular Component(s)
      should
      look something
      like:
    
 
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
  encapsulation: ViewEncapsulation.None
})
    
    
      Note: You may alternatively use the default ViewEncapsulation.Emulated
      value, if you assign additional CSS modifiers :host and ::ng-deep to your class selector. Be warned,
      however, ng-deep is technically deprecated, so this is not best practice.
    
The DataSyncService
      The gojs-angular package comes with an Angular service
      called DataSyncService, used to
      easily merge changes (a go.IncrementalData
      instance) with an Array of Node or Link Data, or a 
        modelData
       object.
    
This service has three static functions:
- 
        
syncNodeData(changes, array)- Merges any node data changes in a go.IncrementalData object with a given array of node data, then returns the new array - 
        
syncLinkData(changes, array)- Merges any link data changes in a go.IncrementalData object with a given array of link data, then returns the new array. Note: Ensure you set the linkKeyProperty if you are using GraphLinksModel, so data merging is possible. - 
        
syncModelData(changes, object)- Merges any modelData changes in a go.IncrementalData object with a given modelData object, then returns the new object 
These functions should allow you to keep your data synced up as needed, without needing to write lots of code.
Listening for Model Changes
      It is common to listen for data changes in a Diagram or Palette, then do
      something with
      those changes on an application-level (such as syncing those changes with app-level data). That's why,
      for both the DiagramComponent
      and
      PaletteComponent, there is a modelChange @Input property function. This is a
      prime example of where the DataSyncService can be used.
    
      As of gojs-angular 2.0, Array / object @Input
      properties are assumed to be immutable. As such, when updating these properties, new Arrays / objects
      must be generated -- one cannot simply mutate an element of the Array or object.
      Please see the gojs-angular-basic
      project for examples of both maintaining state immutability and usage of the DataSyncService.
    
Note: The UndoManager should always be enabled to allow for transactions to take place, but its maxHistoryLength can be set to 0 to prevent undo and redo.
The Diagram and Palette Components
      The Diagram and Palette Components accept a similar set of @Input properties.
    
Diagram Component accepts:
- 
        
initDiagram- A function that must return a GoJS Diagram. You may define your Diagram's Node and Link templates here. divClassName- A class name for your Diagram divnodeDataArray- An array containing data objects for your nodeslinkDataArray- An array containing data objects for your links. Optional.- 
        
modelData- A data object, containing your diagram's model.modelData. Optional. - 
        
skipsDiagramUpdate- A boolean flag, specifying whether the component should skip updating, often set when updating state from a GoJS model change. - 
        
modelChange- A function, which accepts a go.IncrementalData object. This function will fire when your Diagram's model changes, allowing you to decide what to do with those changes. A common practice is to sync your app-level data to reflect the changes in the diagram model, which is made simple using the DataSyncServicegojs-angularships with. 
The Palette Component accepts:
- 
        
initPalette- A function that must return a GoJS Palette. You may define your Palette's Node and Link templates here. divClassName- A class name for the div your Palette divnodeDataArray- An array containing data objects for your nodeslinkDataArray- An array containing data objects for your links. Optional.- 
        
modelData- A data object, containing your palette's model.modelData. Optional. 
      Because GoJS Palettes are read-only by default, there is no modelChange property in PaletteComponent.
      Since there won't be user-driven changes to a Palette's model,
      changes to node/link/model data should be achieved by immutably altering the analogous above @Input properties.
    
Sample Diagram / Palette Component Usage
Here is an example of how one might set up their Diagram / Palette component properties
 
// Big object that holds app-level state data
// As of gojs-angular 2.0, immutability is required of state for change detection
public state = {
  // Diagram state props
  diagramNodeData: [
    { id: 'Alpha', text: "Alpha", color: 'lightblue' },
    { id: 'Beta', text: "Beta", color: 'orange' }
  ],
  diagramLinkData: [
    { key: -1, from: 'Alpha', to: 'Beta' }
  ],
  diagramModelData: { prop: 'value' },
  skipsDiagramUpdate: false,
  // Palette state props
  paletteNodeData: [
    { key: 'PaletteNode1', color: 'firebrick' },
    { key: 'PaletteNode2', color: 'blueviolet' }
  ]
}; // end state object
public diagramDivClassName: string = 'myDiagramDiv';
public paletteDivClassName = 'myPaletteDiv';
// initialize diagram / templates
public initDiagram(): go.Diagram {
  const dia = new go.Diagram({
    'undoManager.isEnabled': true,
    model: new go.GraphLinksModel(
      {
        nodeKeyProperty: 'id',
        linkKeyProperty: 'key' // IMPORTANT! must be defined for merges and data sync when using GraphLinksModel
      }
    )
  });
  // define the Node template
  dia.nodeTemplate =
    new go.Node('Auto')
      .add(
        new go.Shape('RoundedRectangle', { stroke: null })
          .bind('fill', 'color'),
        new go.TextBlock({ margin: 8, editable: true })
          .bindTwoWay('text', 'text')
      );
  return dia;
}
/**
  * Handle GoJS model changes, which output an object of data changes via Mode.toIncrementalData.
  * This method should iterate over thoe changes and update state to keep in sync with the FoJS model.
  * This can be done with any preferred state management method, as long as immutability is preserved.
  */
public diagramModelChange = function(changes: go.IncrementalData) {
  console.log(changes);
  // see gojs-angular-basic for an example model changed handler that preserves immutability
  // when setting state, be sure to set skipsDiagramUpdate: true since GoJS already has this update
};
public initPalette(): go.Palette {
  const palette = new go.Palette();
  // define the Node template
  palette.nodeTemplate =
    new go.Node('Auto')
      .add(
        new go.Shape('RoundedRectangle', { stroke: null })
          .bind('fill', 'color'),
        new go.TextBlock({ margin: 8 })
          .bind('text', 'key')
      );
  palette.model = new go.GraphLinksModel(
    {
      linkKeyProperty: 'key'  // IMPORTANT! must be defined for merges and data sync when using GraphLinksModel
    });
  return palette;
}
    
    
      Once you've defined your @Input properties for your components, pass these properties
      to your DiagramComponent and PaletteComponent in your template, like so:
    
 
<gojs-diagram
  [initDiagram]='initDiagram'
  [nodeDataArray]='state.diagramNodeData'
  [linkDataArray]='state.diagramLinkData'
  [modelData]='state.diagramModelData'
  [skipsDiagramUpdate]='state.skipsDiagramUpdate'
  (modelChange)='diagramModelChange($event)'
  [divClassName]='diagramDivClassName'>
</gojs-diagram>
<gojs-palette
  [initPalette]='initPalette'
  [nodeDataArray]='state.paletteNodeData'
  [divClassName]='paletteDivClassName'>
</gojs-palette>
    
    
      You will now have a GoJS Diagram and Palette working in your Angular application. Again, for a full
      example of a gojs-angular application, see gojs-angular-basic.
    
A Note on Diagram Reinitialization
      Occasionally you may want to treat a model update as if you were loading a completely new model.
      But initialization is only done in your initDiagram function,
      within the DiagramComponent's ngAfterViewInit lifecycle hook, and only once.
      A regular model update is not treated as an initialization, so none of the initial... properties
      of your Diagram will apply.
    
      To address this problem, DiagramComponent exposes a clear method.
      When called, it clears its diagram of all nodes, links, and model data, and
      prepares the next state update to be treated as a diagram initialization.
      That will result in an initial layout and perform initial diagram content alignment and scaling.
      Note that initDiagram is not called again.
    
      Here is a small sample of how one might trigger diagram reinitilization using the clear method with
      gojs-angular 2.0.
    
public reinitModel() {
    this.myDiagramComponent.clear();
    this.state = produce(this.state, draft => {
        draft.skipsDiagramUpdate = false;
        draft.diagramNodeData = [{ id: "Alpha", text: "Zorro", color: "red" }];
        draft.diagramLinkData = [];
    });
}
    
    Using the Overview Component
      The Overview Component accepts the following Angular @Input() properties.
    
initOverview- A function that must return a GoJS Overview.divClassName- A class name for your Overview divobservedDiagram- The GoJS Diagram this Overview observes
Define these properties in the component that will hold your Overview Component, like:
 
public oDivClassName = 'myOverviewDiv';
public initOverview(): go.Overview {
  return new go.Overview();
}
public observedDiagram = null;
    
    Then pass these properties to your Overview Component in your template, like:
 
<gojs-overview
  [initOverview]='initOverview'
  [divClassName]='oDivClassName'
  [observedDiagram]='observedDiagram'>
</gojs-overview>
    
    
      But, we're not done yet. observedDiagram is null, so the Overview won't observe
      anything.
      To assign your Overview a Diagram to observe, you will have to reassign the observedDiagram
      property after initialization. To do so,
      reassign the bound observedDiagram property in your component holding your Overview
      Component in the ngAfterViewInit lifecycle hook.
    
      Note: To avoid a ExpressionChangedAfterItHasBeenCheckedError, you
      must
      inform
      Angular
      to then detect changes.
      This can be done with the ChangeDetectorRef.detectChanges()
      method. You can inject a ChangeDetectorRef instance
      into your wrapper Component constructor, and use that after you alter observedDiagram
      to
      call
      detectChanges(). Like so:
    
 
constructor(private cdr: ChangeDetectorRef) { }
public ngAfterViewInit() {
  if (this.observedDiagram) return;
  // in this snippet, this.myDiagramComponent is a reference to a GoJS/Angular Diagram Component
  // that has a valid GoJS Diagram
  this.observedDiagram = this.myDiagramComponent.diagram;
  // IMPORTANT: without this, Angular will throw ExpressionChangedAfterItHasBeenCheckedError (dev mode only)
  this.cdr.detectChanges();
}
    
    Now, after initialization, your Overview should display appropriately.
Updating Properties Based on App State
      You may have some app-level properties you want to affect the behavior / appearance of your
      Diagram,
      Palette,
      or Overview. You could subclass their respective components and add @Input bindings
      with
      specific
      setter methods, or, more simply, you can have an ngOnChanges function in your
      app-level
      component
      that updates various Diagram / Palette / Component properties based on your app state.
    
      For example, say you have an app-level property called showGrid. When showGrid
      is
      true, your Diagram's grid should be visible -- when false, it should be invisible. In your
      AppComponent, you
      could do something like:
    
 
// myDiagramComponent is a reference to your DiagramComponent
@ViewChild('myDiagram', { static: true }) public myDiagramComponent: DiagramComponent;
public ngOnChanges () {
  // whenever showGrid changes, update the diagram.grid.visible in the child DiagramComponent
  if (this.myDiagramComponent && this.myDiagramComponent.diagram instanceof go.Diagram) {
    this.myDiagramComponent.diagram.grid.visible = this.showGrid;
  }
}
    
    Migrating to 2.0
      This page assumes use of gojs-angular version 2.0, which
      requires
      immutable state, unlike version 1.0. It is recommended to use the 2.0 version. If you have a
      gojs-angular project using version 1.x and want to upgrade, reference this section for tips on
      migrating
      to version 2.
    
Should I Upgrade?
In general, yes.
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 will be published on the 2.x branch moving forward.
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 smaller in file size.
      One may wish to hold off on upgrading if they have lots of operations mutating their @Input
      properties,
      and they do not want to take the
      time to rewrite those operations immutably. However, the guide below details one way one could do this. Our
      gojs-angular-basic sample also
      has demonstrations of immutably updating @Input properties to make such a rewrite easier.
    
Upgrade gojs-angular Version
      Update your package.json to require gojs-angular version 2.0 or greater,
      then run npm install
    
      It is also recommended to upgrade to the latest version of gojs.
    
Immutability
      The biggest change with 2.0 is you must enforce immutability of @Input
      properties to your Diagram and Palette components.
    
      So, for instance, whenever an entry of diagramNodeData
      is updated, removed, or changed, you will need to generate a whole new Array for
      DiagramComponent.diagramNodeData. This can be done in
      many different ways with many different packages. A popular choice is immer, which exposes a produce function
      that allows one to immutability manipulate their data on a draft variable. We will use that function
      here
      for demonstration purposes.
    
The Version 1.0 Way
      In gojs-angular version 1, if you wanted to add some node data to your diagramNodeData
      @Input property,
      you could do so by simply adding to the diagramNodeData, mutating it. Such as:
    
public addNode = function(nodeData: go.ObjectData) {
  // sync changes with GoJS model
  this.skipsDiagramUpdate = false;
  this.diagramNodeData.push(nodeData);
};
    
    The Version 2.0 Way
      In gojs-angular version 2, that same addNode function must be changed so the
      diagramNodeData
      property is updated immutably (that is, replaced with an entirely new Array). Here is an example of
      doing that with immer's produce function.
    
public addNode = function(nodeData: go.ObjectData) {
  this.state = produce(this.state, draft => {
    const nodedata = { id: "Zeta", text: "Zorro", color: "red" };
    draft.skipsDiagramUpdate = false;
    draft.diagramNodeData.push(nodedata);
  });
}
    
    
      Notice we are also using a massive state object to hold gojs-angular component
      properties.
      This makes these kinds
      of immutable operations (especially if you are using immer, or a package like it) straightforward (see how we were
      able to update both skipsDiagramUpdate
      and diagramNodeData on the same draft variable).
    
      To see more samples of enforcing immutability with gojs-angular, see gojs-angular-basic,
      particularly the modelChange property of the Diagram Component.
    
Additional Considerations
      Additionally, PaletteComponent no longer supports skipsPaletteUpdate or modelChange
      properties. As GoJS Palettes are read-only by default, their models should not
      be changing based on user input. Instead, if you need to update their node/link/model data, update their
      @Input properties (immutably, of course).