Assign fields Node Data of Selectable Fields Sample Programmatically

Hi,
How to assign the > fields
node data which are table row items - fetched from getRecords API subscription in my case… And the selectable fields example - fields are static records… Worked with setDataProperty(), insertArrayItem()… but Cannot read properties of undefined (reading ‘fields’) Appreciate your help…

 if (this.fixedConfDiagram) return;
    this.fixedConfDiagram = this.fixedConfComponent.diagram
 // this.fixedConfComponent.diagram.model.nodeDataArray.push(this.confNodeData)
    // this.fixedConfComponent.diagram.model.setDataProperty(this.fixedConfDiagram.model.nodeDataArray.data, "fields", "")
    // this.fixedConfComponent.diagram.model.setDataProperty(this.fixedConfDiagram.model.nodeDataArray.data, "fields1", "")
    // this.fixedConfComponent.diagram.model.addNodeData(this.userNodeData)
    this.fixedConfComponent.diagram.model.insertArrayItem(this.fixedConfDiagram.model.nodeDataArray.data.fields, -1, this.state.confData)
    // this.state.fixedConfNodeData = this.fixedConfComponent.diagram.model.nodeDataArray
    console.log(this.fixedConfComponent.diagram.model.nodeDataArray) //merged
}```

Thanks,
Ganesh

Are you trying to dynamically modify an existing node? I’ll assume that is what you are trying to do.

I will further assume you have the key for the node as well as the new Array of “fields” data.

function updateFields(key, fieldsArray) {
  const data = myDiagram.model.findNodeDataForKey(key);
  if (!data) return;
  myDiagram.model.commit(m => m.set(data, "fields", fieldsArray));
}

Thanks,
Applied All as per selectable fields sample to my Angular project…
But couldn’t see the output in browser… Which line or where am missing out in the below… Just to load with static data is not giving the output… And no error as well in both console and VS code…

import { ChangeDetectorRef, Component, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
import * as go from 'gojs';
import { DiagramComponent } from 'gojs-angular';

const $ = go.GraphObject.make;
@Component({
  selector: 'app-selectable-fields',
  templateUrl: './selectable-fields.component.html',
  styleUrls: ['./selectable-fields.component.css'],
  encapsulation: ViewEncapsulation.ShadowDom
})

export class SelectableFieldsComponent implements OnInit{
  @ViewChild("myDiagram", { static: true })
  public myDiagramComponent: DiagramComponent;
  constructor(private cdr: ChangeDetectorRef) { }
  ngOnInit(): void {
    // throw new Error('Method not implemented.');
    // window.addEventListener('DOMContentLoaded', init);
  }
  public mydiagram: go.Diagram = null;
  public diagramDivClassName: string = "myDiagramDiv";
  // UnselectedBrush = "transparent";  // item appearance, if not "selected"
  // SelectedBrush = "dodgerblue";   // item appearance, if "selected"

  public state = {

    copiesArrays: true,
    copiesArrayObjects: true,
    linkFromPortIdProperty: "fromPort",
    linkToPortIdProperty: "toPort",
    diagramModelData: { prop: "value" },
    skipsDiagramUpdate: false,

    diagramNodeData: [

      {
        key: "Record1",
        fields: [
          { name: "field1", info: "", color: "#F7B84B", figure: "Ellipse" },
          { name: "field2", info: "the second one", color: "#F25022", figure: "Ellipse" },
          { name: "fieldThree", info: "3rd", color: "#00BCF2" }
        ],
        loc: "10 0"
      },
      {
        key: "Record2",
        fields: [
          { name: "fieldA", info: "", color: "#FFB900", figure: "Diamond" },
          { name: "fieldB", info: "", color: "#F25022", figure: "Rectangle" },
          { name: "fieldC", info: "", color: "#7FBA00", figure: "Diamond" },
          { name: "fieldD", info: "fourth", color: "#00BCF2", figure: "Rectangle" }
        ],
        loc: "70 0"
      }
    ]
  }

  public initDiagram() {
    const $ = go.GraphObject.make;

    const dia = $(go.Diagram, {

      validCycle: go.Diagram.CycleNotDirected,  // don't allow loops
      // For this sample, automatically show the state of the diagram's model on the page
      // "ModelChanged": e => {
      //   if (e.isTransactionFinished) this.showModel();
      // },
      "undoManager.isEnabled": true,

      model: $(go.GraphLinksModel, {

        nodeKeyProperty: "key",
        linkKeyProperty: "key", // IMPORTANT! must be defined for merges and data sync when using GraphLinksModel
      }),

    });

        // This template is a Panel that is used to represent each item in a Panel.itemArray.
    // The Panel is data bound to the item object.
    let fieldTemplate =
      $(go.Panel, "TableRow",  // this Panel is a row in the containing Table
        new go.Binding("portId", "name"),  // this Panel is a "port"
        {
          background: "lightgray", //this.UnselectedBrush,  // so this port's background can be picked by the mouse
          fromSpot: go.Spot.LeftRightSides,  // links only go from the right side to the left side
          toSpot: go.Spot.LeftRightSides,
          // allow drawing links from or to this port:
          fromLinkable: true, toLinkable: true
        },
        { // select items -- the background indicates "selected" when not UnselectedBrush
          // click: this.onFieldClick
        },
        $(go.Shape,
          {
            width: 12, height: 12, column: 0, strokeWidth: 2, margin: 4,
            // but disallow drawing links from or to this shape:
            fromLinkable: false, toLinkable: false
          },
          new go.Binding("figure", "figure"),
          new go.Binding("fill", "color")),
        $(go.TextBlock,
          {
            margin: new go.Margin(0, 2), column: 1, font: "bold 13px sans-serif",
            // and disallow drawing links from or to this text:
            fromLinkable: false, toLinkable: false
          },
          new go.Binding("text", "name")),
        $(go.TextBlock,
          { margin: new go.Margin(0, 2), column: 2, font: "13px sans-serif" },
          new go.Binding("text", "info"))
      );

    // This template represents a whole "record".
    dia.nodeTemplate =
      $(go.Node, "Auto",
        new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
        // this rectangular shape surrounds the content of the node
        $(go.Shape,
          { fill: "#EEEEEE" }),
        // the content consists of a header and a list of items
        $(go.Panel, "Vertical",
          // this is the header for the whole node
          $(go.Panel, "Auto",
            { stretch: go.GraphObject.Horizontal },  // as wide as the whole node
            $(go.Shape,
              { fill: "#1570A6", stroke: null }),
            $(go.TextBlock,
              {
                alignment: go.Spot.Center,
                margin: 3,
                stroke: "white",
                textAlign: "center",
                font: "bold 12pt sans-serif"
              },
              new go.Binding("text", "key"))),
          // this Panel holds a Panel for each item object in the itemArray;
          // each item Panel is defined by the itemTemplate to be a TableRow in this Table
          $(go.Panel, "Table",
            {
              name: "TABLE",
              padding: 2,
              minSize: new go.Size(100, 10),
              defaultStretch: go.GraphObject.Horizontal,
              itemTemplate: fieldTemplate
            },
            new go.Binding("itemArray", "fields")
          )  // end Table Panel of items
        )  // end Vertical Panel
      );  // end Node

    dia.linkTemplate =
      $(go.Link,
        { relinkableFrom: true, relinkableTo: true, toShortLength: 4 },  // let user reconnect links
        $(go.Shape, { strokeWidth: 1.5 }),
        $(go.Shape, { toArrow: "Standard", stroke: null })
      );

    return dia;
  }

  public ngAfterViewInit() {
    if (this.mydiagram) return;
    this.mydiagram = this.myDiagramComponent.diagram;
    // this.showModel();
    this.cdr.detectChanges(); // IMPORTANT: without this, Angular will throw ExpressionChangedAfterItHasBeenCheckedError (dev mode only)
    
    const rvdComp: SelectableFieldsComponent = this;
    window.addEventListener('DOMContentLoaded', this.ngOnInit);


    // Override the standard CommandHandler deleteSelection behavior.
    // If there are any selected items, delete them instead of deleting any selected nodes or links.
    // this.diagram.commandHandler.canDeleteSelection = function () {  // method override must be function, not =>
    // true if there are any selected deletable nodes or links,
    // or if there are any selected items within nodes
    // return go.CommandHandler.prototype.canDeleteSelection.call(this) ||
    // this.findAllSelectedItems().length > 0;
    // };
    /*
        this.diagram.commandHandler.deleteSelection = function () {  // method override must be function, not =>
          var items = this.findAllSelectedItems();
          if (items.length > 0) {  // if there are any selected items, delete them
            this.diagram.startTransaction("delete items");
            for (var i = 0; i < items.length; i++) {
              var panel = items[i];
              var nodedata = panel.part.data;
              var itemarray = nodedata.fields;
              var itemdata = panel.data;
              var itemindex = itemarray.indexOf(itemdata);
              this.diagram.model.removeArrayItem(itemarray, itemindex);
            }
            this.diagram.commitTransaction("delete items");
          } else {  // otherwise just delete nodes and/or links, as usual
            go.CommandHandler.prototype.deleteSelection.call(this);
          }
        };
        */
  }

  // isFieldSelected(item) {
  //   return item.background !== this.UnselectedBrush;
  // }

  // setFieldSelected(item, sel) {
  //   if (sel) {
  //     item.background = this.SelectedBrush;
  //   } else {
  //     item.background = this.UnselectedBrush;
  //   }
  // }

  // onFieldClick(e, item) {
  //   var oldskips = item.diagram.skipsUndoManager;
  //   item.diagram.skipsUndoManager = true;
  //   if (e.control || e.meta) {
  //     this.setFieldSelected(item, !this.isFieldSelected(item));
  //     item.part.isSelected = item.panel.elements.any(this.isFieldSelected);
  //   } else if (e.shift) {
  //     // alternative policy: select all fields between this item and some other one??
  //     if (!this.isFieldSelected(item)) this.setFieldSelected(item, true);
  //     item.part.isSelected = true;
  //   } else {
  //     if (!this.isFieldSelected(item)) {
  //       // deselect all sibling items
  //       item.panel.elements.each(it => {
  //         if (it !== item) this.setFieldSelected(it, false);
  //       });
  //       this.setFieldSelected(item, true);
  //     }
  //     item.part.isSelected = true;
  //   }
  //   item.diagram.skipsUndoManager = oldskips;
  // }

  // showModel() {
  //   // throw new Error('Function not implemented.');
    // document.getElementById("mySavedModel").textContent = this.diagram.model.toJson();
  // }

  // this is a bit inefficient, but should be OK for normal-sized graphs with reasonable numbers of items per node
  // findAllSelectedItems() {
  //   var items = [];
  //   for (const nit = this.mydiagram.nodes; nit.next();) {
  //     var node = nit.value;
  //     var table = node.findObject("TABLE");
  //     if (table) {
  //       for (var iit = table.panel.elements; iit.next();) {
  //         var itempanel = iit.value;
  //         if (this.isFieldSelected(itempanel)) items.push(itempanel);
  //       }
  //     }
  //   }
  //   return items;
  // }
}

#HTML

<p>selectable-fields works!</p>
<!-- Diagram id='recordingDiagramDiv -->
<div 
style=" flex-grow: 1; 
    width: 95%;
    height: 450px;
    border: solid 1px #1a252f;
    position: relative;
    -webkit-tap-highlight-color: rgba(255, 255, 255, 0);
  ">
    <gojs-diagram #myDiagram [initDiagram]="initDiagram" [nodeDataArray]="state.diagramNodeData"
        [divClassName]="diagramDivClassName" [modelData]="state.diagramModelData"
        [(skipsDiagramUpdate)]="state.skipsDiagramUpdate">
    </gojs-diagram>
</div>
<!-- <textarea id="mySavedModel" style="width:100%;height:300px"></textarea> -->

Here’s an example of Selectable Fields in Angular: gojs-angular-selectable-fields - StackBlitz

You can compare it to your implementation to find what’s different and what may be causing problems in your app.

That’s great… Thanks for the updated version of Angular Basic with another sample… will work on it to find the corrections needed at my end…

But a quick doubt that why the reload is not dynamic or programmatically using diagramModelChange method (Produce - draft)?. Like Line 263 and 287 are exactly same @ 263 is using Datasync with further details… and @287 is static…

diagramModelChange is for updating state based on changes to the GoJS model that happen in the diagram.

The reload is just a demonstration of doing a full diagram reload with new state, using the clear() method.

Thanks Jhardy and Walter…
Here the selection of item methods are inside initdiagram methods –
isFieldSelected(item), setFieldSelected(item, sel), onFieldClick(e, item) are inside diagram initialization method itself.
and
how to reload the selected items out of this particular (init diagram) method in the same class.
OR in simple
Trying to use the findAllSelectedItems() function and causing errors due to the isFieldSelected(itempanel) method which is inside init as mentioned earlier…

And with that adding the status and the stack trace to overcome the issue

either TypeError C is not a function
resource-planning-view-diagram.component.ts:115 User Nodes from result are
resource-planning-view-diagram.component.ts:119 (5) [{…}, {…}, {…}, {…}, {…}, __gohashid: 1445]
logger.service.ts:10 ERROR: TypeError: c is not a function
at te (go-module.js:287:104)
at push.1963.re.standardMouseClick (go-module.js:285:382)
at push.1963.sg.doMouseUp (go-module.js:467:84)
at push.1963.Oa.doMouseUp (go-module.js:307:288)
at push.1963.T.doMouseUp (go-module.js:597:279)
at a.zk (go-module.js:814:147)
at _ZoneDelegate.invokeTask (zone.js:443:35)
at Object.onInvokeTask (core.mjs:25535:33)
at _ZoneDelegate.invokeTask (zone.js:442:64)
at Zone.runTask (zone.js:214:51)

OR the below users or id is not able to bring out of init method and shows as undefined. Also also not possible to push to an empty array of a class

selectUsers (obj) {
    const itemPanel = obj.panel
    
    if (itemPanel.data.key !== "id") { 
      console.log(itemPanel.data)
      this.addUser = itemPanel.data.name;
      console.log(this.addUser)
      console.log("taskID == " + this.taskID)
    }else {
      this.taskID = itemPanel.data.id
      console.log("taskID == " + this.taskID)
    }
  }

You can always move those functions out of initDiagram if you need to. And maybe you want a two-way binding such that selection state is saved in the model and your app state to make determining selected items easier in your app.

Managed to see the item Arrays of the sample provided and as per your info made the main node and sub item array bindings with make Two Way Binding functions… But still the C type Error is not getting release due to click even function…

Any other way of bindings for selection with help of context menu or other adornment tools ?
For the selection binding… what is the right way for the below line, while I don’t have the fill property in my diagram node data.

new go.Binding("**fill**", "isSelected", sel => { if (sel) return this.setFieldSelected; else return this.isFieldSelected; }).ofObject(""),

Thanks,
Ganesh

“**fill**” is not a property on the Shape class, so that Binding should be producing a warning and have no productive effect.
Assuming the Binding is on a Shape, the target property should be “fill”.

Please start a new forum topic and describe more fully how to reproduce the problem.