TableLayout extension inside a diagram using Angular 13

Hello,

I’ve been trying to make it where the Diagram’s layout is a TableLayout using the TableLayout extension, I keep receiving the same error while starting from the GoJS Angular basic on GitHub.
The code does work when I don’t use TableLayout, when I add TableLayout as a layout

const dia = $(go.Diagram, {
      layout: $(TableLayout),

It gives me the error below, specifically pointing to $(TableLayout).
I have tried to do change parts to how it is done in JS where I add the ID of <gojs-diagram/> when creating the Diagram, but this still results in the same error.

My diagram inside visualisation.component.html looks like this:

<div id="canvas-diagram">
    <gojs-diagram #diagram [initDiagram]="initDiagram" [nodeDataArray]="state.diagramNodeData"
                  [linkDataArray]="state.diagramLinkData"
                  [divClassName]="diagramDivClassName" [modelData]="state.diagramModelData"
                  [skipsDiagramUpdate]="state.skipsDiagramUpdate"
                  (modelChange)="diagramModelChange($event)"></gojs-diagram>
  </div>

Part of my visualisation.component.ts looks like this:

import { ChangeDetectorRef, Component, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
import * as go from 'gojs';
import { TableLayout } from 'gojs/extensionsTS/TableLayout';
import { DataSyncService, DiagramComponent, PaletteComponent } from 'gojs-angular';
import produce from 'immer';

@Component({
  selector: 'app-visualisation',
  templateUrl: './visualisation.component.html',
  styleUrls: ['./visualisation.component.scss'],
  encapsulation: ViewEncapsulation.None
})
export class VisualisationComponent implements OnInit {

  @ViewChild('diagram', { static: true}) public diagramComponent: DiagramComponent;

  observedDiagram = null;
  selectedNodeData: go.ObjectData = null;

  public state = {
    diagramNodeData: [],
    diagramLinkData: [],
    diagramModelData: { prop: 'value' },
    skipsDiagramUpdate: false,
    selectedNodeData: null,
  };

  public diagramDivClassName = 'diagramDiv';

  public initDiagram(): go.Diagram {

    const $ = go.GraphObject.make;
    const dia = $(go.Diagram, {
      layout: $(TableLayout),
      mouseDragOver: e => { e.diagram.currentCursor = 'not-allowed'; },
      mouseDrop: e => { e.diagram.currentTool.doCancel(); },
      'animationManager.isInitial': false,
      'undoManager.isEnabled': true,
      'clickCreatingTool.archetypeNodeData': { text: 'new node' },
      'draggingTool.isGridSnapEnabled': true,
      grid: $(go.Panel, 'Grid',
        { gridCellSize: new go.Size(88, 88) },
        $(go.Shape, 'LineH', { stroke: 'lightgray'}),
        $(go.Shape, 'LineV', { stroke: 'lightgray'})),
      model: $(go.GraphLinksModel,
        {
          nodeKeyProperty: 'id',
          linkKeyProperty: 'key' // IMPORTANT! must be defined for merges and data sync when using GraphLinksModel
        }
      )
    });

    dia.commandHandler.archetypeGroupData = { key: 'Group', isGroup: true};
    dia.toolManager.draggingTool.isGridSnapEnabled = true;

    dia.nodeTemplate =
      $(go.Node, 'Vertical', { desiredSize: new go.Size(88, 88), resizeObjectName: 'FBNODE'
          /*mouseDragEnter: (e, node) => {
            e.handled = true;
            node.findObject('FBNODE').fill = 'red';
            e.diagram.currentCursor = 'not-allowed';
          }
        mouseDrop: (e, node) => node.diagram.currentTool.doCancel()*/},
        $(go.Panel, 'Auto',
          { background: 'white', maxSize: new go.Size(88, 88) },
          $(go.Shape, 'Rectangle',
            { fill: '#FFFFFF', stroke: 'rgba(0, 0, 0, 0.15)', width: 88, height: 88 }),
          $(go.Panel, 'Vertical',   // Everything within the border
            $(go.Picture,
              { desiredSize: new go.Size(64, 64), scale: 1, margin: new go.Margin(4,12,0,12) },
              new go.Binding('source', 'type', type => 'assets/functionblocks/outputs/' + type.toLowerCase() + '/0/_black.svg')),
            $(go.TextBlock,
              { stretch: go.GraphObject.Horizontal, textAlign: 'center', font: '12pt', wrap: go.TextBlock.WrapDesiredSize, overflow: go.TextBlock.OverflowEllipsis },
              new go.Binding('text', 'name'))
          )
        )

      );

    dia.nodeTemplateMap.add('col_head',
      $(go.Part, 'Spot',
        {
          row: 0, rowSpan: 9999, column: 1,
          size: new go.Size(88, 88),
          movable: false,
          resizable: false,
        },
        new go.Binding('column', 'col'),
        $(go.Panel, 'Auto',
          { // this is positioned above the Shape, in row 1
            alignment: go.Spot.Top, alignmentFocus: go.Spot.Bottom,
            stretch: go.GraphObject.Horizontal,
            height: 88
          },
          $(go.Shape, { fill: 'transparent', strokeWidth: 0 }),
          $(go.TextBlock,
            {
              font: '12pt', isMultiline: false,
              wrap: go.TextBlock.None, overflow: go.TextBlock.OverflowEllipsis
            },
            new go.Binding('text'))
        )));

    dia.nodeTemplateMap.add('row_head',  // for each row header
      $(go.Part, 'Spot',
        {
          row: 1, column: 0, columnSpan: 9999,
          height: 88,
          movable: false,
          resizable: false,
        },
        new go.Binding('row'),
        $(go.Panel, 'Auto',
          { // this is positioned to the left of the Shape, in column 1
            alignment: go.Spot.Left, alignmentFocus: go.Spot.Right,
            height: 88
          },
          $(go.Shape, { fill: 'transparent', strokeWidth: 0 }),
          $(go.TextBlock,
            {
              font: '12pt', isMultiline: false,
              wrap: go.TextBlock.None, overflow: go.TextBlock.OverflowEllipsis
            },
            new go.Binding('text'))
        )
      ));

    return dia;
  }

  constructor(private cdr: ChangeDetectorRef) { }

  ngOnInit() {
      // TEMP TILL VISUALISATION CAN BE REQUESTED FROM DATABASE
      const visualisationExists = false;
      if(visualisationExists) {

      } else {
        this.state.diagramNodeData = [
          // Column and row headers
          { key: 'header_1', text: 'Change Me!', col: 1, category: 'col_head'},
          { key: 'row_1', text: 'Change Me!', row: 1, category: 'row_head'},
          // Cells
          //{ key: 'cell_1_1', row: 2, col: 2, isGroup: true } // TODO: Check why isGroup is needed
        ];
      }
  }
}
core.mjs:6485 ERROR Error: Uncaught (in promise): Error: GraphObject.make requires a class function or GoJS class name or name of an object builder, not: undefined
Error: GraphObject.make requires a class function or GoJS class name or name of an object builder, not: undefined
    at C (go-module.js:13:65)
    at Bl (go-module.js:929:306)
    at DiagramComponent.initDiagram (visualisation.component.ts:34:15)
    at DiagramComponent.ngAfterViewInit (gojs-angular.js:224:1)
    at callHook (core.mjs:2542:1)
    at callHooks (core.mjs:2511:1)
    at executeInitAndCheckHooks (core.mjs:2462:1)
    at refreshView (core.mjs:9555:1)
    at refreshComponent (core.mjs:10655:1)
    at refreshChildComponents (core.mjs:9280:1)
    at resolvePromise (zone.js:1262:1)
    at resolvePromise (zone.js:1216:1)
    at zone.js:1329:1
    at _ZoneDelegate.push.3484._ZoneDelegate.invokeTask (zone.js:443:1)
    at Object.onInvokeTask (core.mjs:25535:1)
    at _ZoneDelegate.push.3484._ZoneDelegate.invokeTask (zone.js:442:1)
    at Zone.push.3484.Zone.runTask (zone.js:214:1)
    at drainMicroTaskQueue (zone.js:632:1)

I’m using Ionic using Angular, inside an Electron application:

    "@angular/common": "~13.2.2",
    "@angular/core": "~13.2.2",
    "@ionic/angular": "^6.0.0",
    "electron": "^17.1.2",
    "gojs": "^2.2.6",
    "gojs-angular": "^2.0.4",

My end goal is basically where I can drag node’s from a palette into a diagram that is a TableLayout, where I would be able to add column’s & cells by clicking on a button. Each cell would then only be able to have one node. No node’s can be placed outside the table.

Could you please guide me to the right approach?

It looks like it was unable to load the extension file properly. We recommend that you copy any extension code into your project and that you modify its import statement to match how you are importing the GoJS library.

1 Like

Thank you, this did solve my issue. For anyone who encounters this same problem, this is what I did:

  1. Create a class named TableLayout and copy all the content from /node_modules/gojs/extensionsTS/TableLayout.ts in the new .ts file.

  2. import the TableLayout class where you want to use a diagram with a TableLayout:
    import { TableLayout } from 'src/app/class/tablelayout';.

  3. Create a diagram with TableLayout as the layout:

const diagram = $(go.Diagram,{
      layout: $(TableLayout,
        // ...
      )
    });