Most of time diagram render but once a while when i restart without any code changes, the diagram does not render.
the code below is similar to sample code except I am using TreeViewModel and fetching nodes and links data from API. I believe i need to trigger to diagram once data is fetched and not sure if once data is fetched i need to call this.mydiagramodel.modechanges.emit() or is there a different way to notify diagram that the data has been fetched so it should render. Also, I dont see ngAfterViewInit getting triggered.
Your help is appreciated.
import { ChangeDetectorRef, Component, OnInit, OnChanges, ViewChild, ViewEncapsulation, AfterViewInit } from ‘@angular/core’;
import { ActivatedRoute } from ‘@angular/router’;
import * as go from ‘gojs’;
import { DataSyncService, DiagramComponent, PaletteComponent } from ‘gojs-angular’;
import * as _ from ‘lodash’;
import { forkJoin } from ‘rxjs’;
import { IVRNode } from ‘src/app/models/Node’;
import { IVRLink } from ‘src/app/models/NodeLink’;
import { NodeService } from ‘…/…/services/node.service’;
@Component({
selector: ‘app-viewchart’,
templateUrl: ‘./viewchart.component.html’,
styleUrls: [’./viewchart.component.scss’],
encapsulation: ViewEncapsulation.ShadowDom
})
export class ViewChartComponent implements OnInit, AfterViewInit{
@ViewChild(‘myDiagram’, { static: true }) public myDiagramComponent: DiagramComponent;
@ViewChild(‘myPalette’, { static: true }) public myPaletteComponent: PaletteComponent;
accountid: string;
public diagramNodeData: Array<go.ObjectData>;
public diagramLinkData: Array<go.ObjectData> ;
public diagramDivClassName: string = ‘myDiagramDiv’;
public diagramModelData = { prop: ‘value’ };
public skipsDiagramUpdate = false;
// When the diagram model changes, update app data to reflect those changes
public diagramModelChange = function(changes: go.IncrementalData) {
// when setting state here, be sure to set skipsDiagramUpdate: true since GoJS already has this update
// (since this is a GoJS model changed listener event function)
// this way, we don't log an unneeded transaction in the Diagram's undoManager history
this.skipsDiagramUpdate = true;
this.diagramNodeData = DataSyncService.syncNodeData(changes, this.diagramNodeData);
this.diagramLinkData = DataSyncService.syncLinkData(changes, this.diagramLinkData);
this.diagramModelData = DataSyncService.syncModelData(changes, this.diagramModelData);
};
constructor(private cdr: ChangeDetectorRef, private nodeservice: NodeService, private route: ActivatedRoute) {
this.route.paramMap.subscribe(
params => {
console.log(params.get('id'));
this.accountid = params.get('id');
}
);
}
ngOnInit(): void {
let nodedata = this.nodeservice.getnodes(this.accountid);
let linksdata = this.nodeservice.getlinks(this.accountid);
forkJoin([nodedata, linksdata]).subscribe(results => {
this.diagramNodeData = results[0];
this.diagramLinkData = results[1];
console.log(this.diagramNodeData);
console.log(this.diagramLinkData);
this.myDiagramComponent.modelChange.emit();
});
console.log('ngonit ends');
}
ngOnChanges(): void
{
console.log('ngonchanges');
}
ngAfterViewInit(): void {
console.log("after view init");
if (this.observedDiagram) return;
this.observedDiagram = this.myDiagramComponent.diagram;
this.cdr.detectChanges(); // IMPORTANT: without this, Angular will throw ExpressionChangedAfterItHasBeenCheckedError (dev mode only)
const appComp: ViewChartComponent = this;
// listener for inspector
this.myDiagramComponent.diagram.addDiagramListener('ChangedSelection', function(e) {
if (e.diagram.selection.count === 0) {
appComp.selectedNode = null;
}
const node = e.diagram.selection.first();
if (node instanceof go.Node) {
appComp.selectedNode = node;
} else {
appComp.selectedNode = null;
}
});
} // end ngAfterViewInit
// initialize diagram / templates
public initDiagram(): go.Diagram
{
const $ = go.GraphObject.make;
const dia = $(go.Diagram, {
'undoManager.isEnabled': true,
allowCopy: false,
"draggingTool.dragsTree": true,
"commandHandler.deletesTree": true,
layout:
$(go.TreeLayout,
{ angle: 90, arrangement: go.TreeLayout.ArrangementFixedRoots }),
model: $(go.GraphLinksModel,
{
linkToPortIdProperty: 'toPort',
linkFromPortIdProperty: 'fromPort',
linkKeyProperty: 'key' // IMPORTANT! must be defined for merges and data sync when using GraphLinksModel
}
)
});
dia.commandHandler.archetypeGroupData = { key: 'Group', isGroup: true };
const makePort = function(id: string, spot: go.Spot) {
return $(go.Shape, 'Circle',
{
opacity: .5,
fill: 'gray', strokeWidth: 0, desiredSize: new go.Size(8, 8),
portId: id, alignment: spot,
fromLinkable: true, toLinkable: true
}
);
}
var bluegrad = $(go.Brush, "Linear", { 0: "#C4ECFF", 1: "#70D4FF" });
var greengrad = $(go.Brush, "Linear", { 0: "#B1E2A5", 1: "#7AE060" });
// each action is represented by a shape and some text
var actionTemplate =
$(go.Panel, "Horizontal",
$(go.Shape,
{ width: 12, height: 12 },
new go.Binding("figure"),
new go.Binding("fill")
),
$(go.TextBlock,
{ font: "10pt Verdana, sans-serif" },
new go.Binding("text")
)
);
// define the Node template
/*
dia.nodeTemplate =
$(go.Node, 'Spot',
{
contextMenu:
$('ContextMenu',
$('ContextMenuButton',
$(go.TextBlock, 'Group'),
{ click: function(e, obj) { e.diagram.commandHandler.groupSelection(); } },
new go.Binding('visible', '', function(o) {
return o.diagram.selection.count > 1;
}).ofObject())
)
},
$(go.Panel, 'Auto',
$(go.Shape, 'RoundedRectangle', { stroke: null },
new go.Binding('fill', 'color')
),
$(go.TextBlock, { margin: 8 },
new go.Binding('question'))
),
// Ports
makePort('t', go.Spot.TopCenter),
makePort('l', go.Spot.Left),
makePort('r', go.Spot.Right),
makePort('b', go.Spot.BottomCenter)
);
*/
dia.nodeTemplate = // the default node template
$(go.Node, "Vertical",
new go.Binding("isTreeExpanded").makeTwoWay(), // remember the expansion state for
new go.Binding("wasTreeExpanded").makeTwoWay(), // when the model is re-loaded
{ selectionObjectName: "BODY" },
// the main "BODY" consists of a RoundedRectangle surrounding nested Panels
$(go.Panel, "Auto",
{ name: "BODY" },
$(go.Shape, "Rectangle",
{ fill: bluegrad, stroke: null }
),
$(go.Panel, "Vertical",
{ margin: 3 },
// the title
$(go.TextBlock,
{
stretch: go.GraphObject.Horizontal,
font: "bold 12pt Verdana, sans-serif"
},
new go.Binding("text", "question")
),
// the optional list of actions
$(go.Panel, "Vertical",
{ stretch: go.GraphObject.Horizontal, visible: false }, // not visible unless there is more than one action
new go.Binding("visible", "actions", function(acts) {
return (Array.isArray(acts) && acts.length > 0);
}),
// headered by a label and a PanelExpanderButton inside a Table
$(go.Panel, "Table",
{ stretch: go.GraphObject.Horizontal },
$(go.TextBlock, "Choices",
{
alignment: go.Spot.Left,
font: "10pt Verdana, sans-serif"
}
),
$("PanelExpanderButton", "COLLAPSIBLE", // name of the object to make visible or invisible
{ column: 1, alignment: go.Spot.Right }
)
), // end Table panel
// with the list data bound in the Vertical Panel
$(go.Panel, "Vertical",
{
name: "COLLAPSIBLE", // identify to the PanelExpanderButton
padding: 2,
stretch: go.GraphObject.Horizontal, // take up whole available width
background: "white", // to distinguish from the node's body
defaultAlignment: go.Spot.Left, // thus no need to specify alignment on each element
itemTemplate: actionTemplate // the Panel created for each item in Panel.itemArray
},
new go.Binding("itemArray", "actions") // bind Panel.itemArray to nodedata.actions
) // end action list Vertical Panel
) // end optional Vertical Panel
) // end outer Vertical Panel
), // end "BODY" Auto Panel
$(go.Panel, // this is underneath the "BODY"
{ height: 17 }, // always this height, even if the TreeExpanderButton is not visible
$("TreeExpanderButton")
),
makePort('b', go.Spot.BottomCenter)
);
// define a second kind of Node:
dia.nodeTemplateMap.add("Terminal",
$(go.Node, "Spot",
$(go.Shape, "Circle",
{ width: 55, height: 55, fill: greengrad, stroke: null }
),
$(go.TextBlock,
{ font: "10pt Verdana, sans-serif" },
new go.Binding("text")
)
)
);
return dia;
}
/*.
public diagramNodeData: Array<go.ObjectData> = [
{ key: 'Alpha', text: "Node Alpha", color: 'lightblue' },
{ key: 'Beta', text: "Node Beta", color: 'orange' },
{ key: 'Gamma', text: "Node Gamma", color: 'lightgreen' },
{ key: 'Delta', text: "Node Delta", color: 'pink' }
];
*/
public initPalette(): go.Palette {
const $ = go.GraphObject.make;
const palette = $(go.Palette);
// define the Node template
palette.nodeTemplate =
$(go.Node, 'Auto',
$(go.Shape, 'RoundedRectangle',
{
stroke: null
},
new go.Binding('fill', 'color')
),
$(go.TextBlock, { margin: 8 },
new go.Binding('text'))
);
palette.model = $(go.GraphLinksModel,
{
linkKeyProperty: 'key' // IMPORTANT! must be defined for merges and data sync when using GraphLinksModel
});
return palette;
}
public paletteNodeData: Array<go.ObjectData> = [
{ key: 'PaletteNode1', text: "PaletteNode1", color: 'red' },
{ key: 'PaletteNode2', text: "PaletteNode2", color: 'yellow' }
];
public paletteLinkData: Array<go.ObjectData> = [
{ }
];
public paletteModelData = { prop: ‘val’ };
public paletteDivClassName = ‘myPaletteDiv’;
public skipsPaletteUpdate = false;
public paletteModelChange = function(changes: go.IncrementalData) {
// when setting state here, be sure to set skipsPaletteUpdate: true since GoJS already has this update
// (since this is a GoJS model changed listener event function)
// this way, we don't log an unneeded transaction in the Palette's undoManager history
this.skipsPaletteUpdate = true;
this.paletteNodeData = DataSyncService.syncNodeData(changes, this.paletteNodeData);
this.paletteLinkData = DataSyncService.syncLinkData(changes, this.paletteLinkData);
this.paletteModelData = DataSyncService.syncModelData(changes, this.paletteModelData);
};
// Overview Component testing
public oDivClassName = ‘myOverviewDiv’;
public initOverview(): go.Overview {
const $ = go.GraphObject.make;
const overview = $(go.Overview);
return overview;
}
public observedDiagram = null;
// currently selected node; for inspector
public selectedNode: go.Node | null = null;
public handleInspectorChange(newNodeData) {
const key = newNodeData.key;
// find the entry in nodeDataArray with this key, replace it with newNodeData
let index = null;
for (let i = 0; i < this.diagramNodeData.length; i++) {
const entry = this.diagramNodeData[i];
if (entry.key && entry.key === key) {
index = i;
}
}
if (index >= 0) {
// here, we set skipsDiagramUpdate to false, since GoJS does not yet have this update
this.skipsDiagramUpdate = false;
this.diagramNodeData[index] = _.cloneDeep(newNodeData);
// this.diagramNodeData[index] = _.cloneDeep(newNodeData);
}
// var nd = this.observedDiagram.model.findNodeDataForKey(newNodeData.key);
// console.log(nd);
}
}