Link's "fromPort" and "toPort" properties not appears in linkDataArray of Diagram Model

I am using https://gojs.net/latest/samples/draggableLink.html GoJS diagram. How can i get fromPort and toPort in linkDataArray?

Below is my Link template code for the same.

let linkSelectionAdornmentTemplate: go.Adornment = $(
      go.Adornment,
      "Link",
      $(
        go.Shape,
        // isPanelMain declares that this Shape shares the Link.geometry
        { isPanelMain: true, fill: null, stroke: "deepskyblue", strokeWidth: 0 }
      ) // use selection object's strokeWidth
    );

    this.diagram.linkTemplate = $(
      go.Link,
      // allow relinking
      {
        selectable: true,
        selectionAdornmentTemplate: linkSelectionAdornmentTemplate,
        fromSpot: this.diagram.linkTemplate.fromSpot
      },
      { relinkableFrom: true, relinkableTo: true, reshapable: true },
      {
        routing: go.Link.AvoidsNodes,
        curve: go.Link.JumpOver,
        corner: 5,
        toShortLength: 4,
      },
      new go.Binding("points").makeTwoWay(), // this line needs to commented for responsive links rendering
      $(
        go.Shape, // the link path shape
        { isPanelMain: true, strokeWidth: 2 }
      ),
      $(
        go.Shape, // the arrowhead
        { toArrow: "Standard", stroke: null }
      ),
      $(
        go.Panel,
        "Auto",
        new go.Binding("visible").ofObject(),
        $(
          go.Shape,
          "RoundedRectangle", // the link shape
          { fill: null, stroke: null }
        ),
        $(
          go.TextBlock,
          {
            textAlign: "center",
            font: "bold 10pt helvetica, arial, sans-serif",
            margin: 2,
            minSize: new go.Size(10, NaN),
            editable: true,
          },
          new go.Binding("text").makeTwoWay()
        )
      )
    );

After creating wireframe i am not able to get fromPort and toPort in linkDataArray of Diagram Model.

I am expecting output like below screen shot.

But getting model without fromPort and toPort in linkDataArray as shown in below screen shot.

currentGoJS

Note the property values of the GraphLinksModel in our screenshot. Have you set:

It would be best to set those two properties before you set the linkDataArray.

Can you provide any example for the same? Because in reference link i can’t found any code for it still those properties are appearing in model data.

Just search the samples for either of those property names.

How are you creating the GraphLinksModel instance that you are setting as the value of Diagram.model? That is when it would make sense to set those properties.

Below is my code for Diagram.model.

const $ = go.GraphObject.make;
    // Place GoJS license key here:
    (go as any).Diagram.licenseKey = Constants.GOJS_LICENSE_KEY;
    this.diagram = new go.Diagram();
    this.diagram.initialContentAlignment = go.Spot.TopLeft;
    this.diagram.allowDrop = true;
    this.diagram.allowVerticalScroll = false;
    this.diagram.allowHorizontalScroll = false;
    this.diagram.allowZoom = false;
    this.diagram.undoManager.isEnabled = true;
    this.diagram.toolManager.draggingTool = new GuidedDraggingTool();
    this.diagram.toolManager.linkingTool.isUnconnectedLinkValid = true;
    this.diagram.toolManager.relinkingTool.isUnconnectedLinkValid = true;
    this.diagram.toolManager.draggingTool.dragsLink = true;
    this.diagram.animationManager.isEnabled = false;

That’s the Diagram – I see no mention of anything to do with a model.

For loading model i am using this code this.diagram.model = go.Model.fromJson(wireFrameData);

Below is my all code for GoJS component.

export class DiagramEditorComponent implements OnInit {
  private diagram: go.Diagram = new go.Diagram();
  private palette: go.Palette = new go.Palette();
  private nodeObject: go.ObjectData;
  private nodeColor: string;
  public nodeTextColor: string;
  private firstUpdateSkip: number = environment.wireframe_block_Update_Counter;
  private selectedNodes = [];
  private selectedLinks = [];
  public nodeTextSize: number;
  public nodeTextStyle: string;
  public nodeTextFamily: string = "Helvetica";
  public nodeTextFontFamily = ["Helvetica", "sans-serif", "Arial"];

  @ViewChild("diagramDiv")
  private diagramRef: ElementRef;

  @ViewChild("paletteDiv")
  private paletteRef: ElementRef;

  @ViewChild("nodeColorPicker")
  private colorPickerRef: ElementRef;

  @ViewChild("nodeTextChange")
  private changeNodeTextRef: ElementRef;

  // @Input()
  // get model(): go.Model { return this.diagram.model; }
  // set model(val: go.Model) { this.diagram.model = val; }
  @Input() wireframeData: go.Model;
  @Input() zIndex: number;
  @Input() row: number;
  @Input() col: number;
  @Input() pointerEvent: boolean;
  @Output() onBackgroundClick: EventEmitter<void> = new EventEmitter();

  @Output()
  nodeSelected = new EventEmitter<go.Node | null>();

  @Output()
  modelChanged = new EventEmitter<go.Model>();

  private nodeBlinkSubscription: Subscription;
  private pasteClipWireframeSubscription: Subscription;
  private copyPasteWireframeSubscription: Subscription;

  constructor(
    private modalService: NgbModal,
    private commonService: CommonService
  ) {
    const $ = go.GraphObject.make;
    // Place GoJS license key here:
    (go as any).Diagram.licenseKey = Constants.GOJS_LICENSE_KEY;
    this.diagram = new go.Diagram();
    this.diagram.initialContentAlignment = go.Spot.TopLeft;
    this.diagram.allowDrop = true;
    this.diagram.allowVerticalScroll = false;
    this.diagram.allowHorizontalScroll = false;
    this.diagram.allowZoom = false;
    this.diagram.undoManager.isEnabled = true;
    this.diagram.toolManager.draggingTool = new GuidedDraggingTool();
    this.diagram.toolManager.linkingTool.isUnconnectedLinkValid = true;
    this.diagram.toolManager.relinkingTool.isUnconnectedLinkValid = true;
    this.diagram.toolManager.draggingTool.dragsLink = true;
    this.diagram.animationManager.isEnabled = false;

    this.diagram.addDiagramListener("ChangedSelection", (e) => {
      const node = e.diagram.selection.first();
      this.nodeSelected.emit(node instanceof go.Node ? node : null);
    });

    this.diagram.addDiagramListener(
      "ExternalObjectsDropped",
      this.setScreenHeightWidthOnNode
    );
    this.diagram.addDiagramListener(
      "LinkDrawn",
      this.setScreenHeightWidthOnNode
    );
    this.diagram.addDiagramListener(
      "SelectionMoved",
      this.setScreenHeightWidthOnNode
    );
    this.diagram.addDiagramListener(
      "SelectionCopied",
      this.setScreenHeightWidthOnNode
    );
    this.diagram.addDiagramListener(
      "PartResized",
      this.setScreenHeightWidthOnNode
    );
    this.diagram.addDiagramListener(
      "ChangedSelection",
      this.onItemSelect.bind(this)
    );
    this.diagram.addDiagramListener(
      "BackgroundSingleClicked",
      this.onBgClick.bind(this)
    );

    this.diagram.addModelChangedListener(
      (e) => e.isTransactionFinished && this.updateWireframeData(e)
    );

    let nodeSelectionAdornmentTemplate: go.Adornment = $(
      go.Adornment,
      "Auto",
      $(go.Shape, {
        fill: null,
        stroke: "deepskyblue",
        strokeWidth: 1.5,
        strokeDashArray: [4, 2],
      }),
      $(go.Placeholder)
    );

    let nodeResizeAdornmentTemplate: go.Adornment = $(
      go.Adornment,
      "Spot",
      { locationSpot: go.Spot.Right },
      $(go.Placeholder),
      $(go.Shape, {
        alignment: go.Spot.TopLeft,
        cursor: "nw-resize",
        desiredSize: new go.Size(6, 6),
        fill: "lightblue",
        stroke: "deepskyblue",
      }),
      $(go.Shape, {
        alignment: go.Spot.Top,
        cursor: "n-resize",
        desiredSize: new go.Size(6, 6),
        fill: "lightblue",
        stroke: "deepskyblue",
      }),
      $(go.Shape, {
        alignment: go.Spot.TopRight,
        cursor: "ne-resize",
        desiredSize: new go.Size(6, 6),
        fill: "lightblue",
        stroke: "deepskyblue",
      }),

      $(go.Shape, {
        alignment: go.Spot.Left,
        cursor: "w-resize",
        desiredSize: new go.Size(6, 6),
        fill: "lightblue",
        stroke: "deepskyblue",
      }),
      $(go.Shape, {
        alignment: go.Spot.Right,
        cursor: "e-resize",
        desiredSize: new go.Size(6, 6),
        fill: "lightblue",
        stroke: "deepskyblue",
      }),

      $(go.Shape, {
        alignment: go.Spot.BottomLeft,
        cursor: "se-resize",
        desiredSize: new go.Size(6, 6),
        fill: "lightblue",
        stroke: "deepskyblue",
      }),
      $(go.Shape, {
        alignment: go.Spot.Bottom,
        cursor: "s-resize",
        desiredSize: new go.Size(6, 6),
        fill: "lightblue",
        stroke: "deepskyblue",
      }),
      $(go.Shape, {
        alignment: go.Spot.BottomRight,
        cursor: "sw-resize",
        desiredSize: new go.Size(6, 6),
        fill: "lightblue",
        stroke: "deepskyblue",
      })
    );

    let nodeRotateAdornmentTemplate: go.Adornment = $(
      go.Adornment,
      { locationSpot: go.Spot.Center, locationObjectName: "CIRCLE" },
      $(go.Shape, "Circle", {
        name: "CIRCLE",
        cursor: "pointer",
        desiredSize: new go.Size(7, 7),
        fill: "lightblue",
        stroke: "deepskyblue",
      }),
      $(go.Shape, {
        geometryString: "M3.5 7 L3.5 30",
        isGeometryPositioned: true,
        stroke: "deepskyblue",
        strokeWidth: 1.5,
        strokeDashArray: [4, 2],
      })
    );

    this.diagram.nodeTemplate = $(
      go.Node,
      "Spot",
      { locationSpot: go.Spot.Center },
      new go.Binding("location", "dloc", go.Point.parse).makeTwoWay(
        go.Point.stringify
      ),
      {
        selectable: true,
        selectionAdornmentTemplate: nodeSelectionAdornmentTemplate,
      },
      {
        resizable: true,
        resizeObjectName: "PANEL",
        resizeAdornmentTemplate: nodeResizeAdornmentTemplate,
      },
      { rotatable: true, rotateAdornmentTemplate: nodeRotateAdornmentTemplate },
      new go.Binding("angle").makeTwoWay(),
      // the main object is a Panel that surrounds a TextBlock with a Shape
      $(
        go.Panel,
        "Auto",
        { name: "PANEL" },
        new go.Binding("desiredSize", "size", go.Size.parse).makeTwoWay(
          go.Size.stringify
        ),
        $(
          go.Shape,
          "Rectangle", // default figure
          {
            portId: "", // the default port: if no spot on link data, use closest side
            fromLinkable: true,
            toLinkable: true,
            cursor: "pointer",
            fill: "white", // default color
          },
          new go.Binding("figure"),
          new go.Binding("fill")
        ),
        $(
          go.TextBlock,
          {
            font: "bold 11pt Helvetica, Arial, sans-serif",
            margin: 8,
            //maxSize: new go.Size(160, NaN),
            wrap: go.TextBlock.WrapFit,
            editable: true,
          },
          new go.Binding("text").makeTwoWay(),
          new go.Binding("stroke"),
          new go.Binding("font")
        ),
        {
          // define a context menu for each node
          contextMenu: $(
            go.Adornment,
            "Vertical", // that has one button
            $("ContextMenuButton", $(go.TextBlock, "Change BG Color"), {
              click: this.changeColor.bind(this),
            }),
            $("ContextMenuButton", $(go.TextBlock, "Change Font"), {
              click: this.changeFont.bind(this),
            })
            // more ContextMenuButtons would go here
          ), // end Adornment
        }
      ),
      // four small named ports, one on each side:
      this.makePort("T", go.Spot.Top, false, true),
      this.makePort("L", go.Spot.Left, true, true),
      this.makePort("R", go.Spot.Right, true, true),
      this.makePort("B", go.Spot.Bottom, true, false),
      {
        // handle mouse enter/leave events to show/hide the ports
        mouseEnter: (e, node) => {
          this.showSmallPorts(node, true);
        },
        mouseLeave: (e, node) => {
          this.showSmallPorts(node, false);
        },
      }
    );

    let linkSelectionAdornmentTemplate: go.Adornment = $(
      go.Adornment,
      "Link",
      $(
        go.Shape,
        // isPanelMain declares that this Shape shares the Link.geometry
        { isPanelMain: true, fill: null, stroke: "deepskyblue", strokeWidth: 0 }
      ) // use selection object's strokeWidth
    );

    this.diagram.linkTemplate = $(
      go.Link,
      // allow relinking
      {
        selectable: true,
        selectionAdornmentTemplate: linkSelectionAdornmentTemplate,
      },
      { relinkableFrom: true, relinkableTo: true, reshapable: true },
      {
        routing: go.Link.AvoidsNodes,
        curve: go.Link.JumpOver,
        corner: 5,
        toShortLength: 4,
      },
      new go.Binding("points").makeTwoWay(), // this line needs to commented for responsive links rendering
      $(
        go.Shape, // the link path shape
        { isPanelMain: true, strokeWidth: 2 }
      ),
      $(
        go.Shape, // the arrowhead
        { toArrow: "Standard", stroke: null }
      ),
      $(
        go.Panel,
        "Auto",
        new go.Binding("visible").ofObject(),
        $(
          go.Shape,
          "RoundedRectangle", // the link shape
          { fill: null, stroke: null }
        ),
        $(
          go.TextBlock,
          {
            textAlign: "center",
            font: "bold 10pt helvetica, arial, sans-serif",
            margin: 2,
            minSize: new go.Size(10, NaN),
            editable: true,
          },
          new go.Binding("text").makeTwoWay()
        )
      )
    );

    let figures = new Figures();
    figures.defineBpmnTaskUserIcon();
    figures.defineDatabaseIcon();
    figures.defineLinedDocIcon();
    figures.defineEthernetIcon();
    figures.defineFiveBarUserIcon();
    figures.defineWarhouseIcon();

    this.palette = new go.Palette();
    this.palette.nodeTemplateMap = this.diagram.nodeTemplateMap;
    (this.palette.layout = $(go.GridLayout, {
      cellSize: new go.Size(1, 1),
      spacing: new go.Size(5, 5),
      wrappingColumn: 12,
    })),
      // initialize contents of Palette
      (this.palette.model.nodeDataArray = [
        { text: "Org", figure: "Warehouse", fill: "lightgreen" },
        {
          text: "Role",
          figure: "BpmnTaskUser",
          fill: "orange"
        },
        { text: "Start", figure: "Circle", fill: "green" },
        { text: "Step" },
        { text: "???", figure: "Diamond", fill: "lightskyblue" },
        { text: "Document", figure: "LinedDocument", fill: "pink" },
        { text: "DB", figure: "Database", fill: "lightgray" },
        { text: "System", figure: "Ethernet", fill: "darkgray" },
        { text: "End", figure: "Circle", fill: "red" },
        { text: "Comment", figure: "RoundedRectangle", fill: "lightyellow" },
        { text: "Measure", figure: "5Bars", fill: "pink" },
      ]);
  }

  ngOnInit() {
    this.diagram.div = this.diagramRef.nativeElement;
    //this.palette.div = this.paletteRef.nativeElement;
    this.palette.div = <HTMLDivElement>document.getElementById("paletteDiv");
    this.nodeBlinkSubscription = this.commonService.blinkWireframeNode.subscribe(
      (nodeIndex: number) => {
        console.log("wireframe animation");
        let i = 0;
        let animation = new go.Animation();
        this.diagram.nodes.each((node) => {
          if (i == nodeIndex) {
            animation.add(node, "scale", node.scale, 2);
            animation.start();

            setTimeout(() => {
              let sanimation = new go.Animation();
              sanimation.add(node, "scale", node.scale, 1);
              sanimation.start();
            }, 700);
          }
          i++;
        });
      }
    );

    this.copyPasteWireframeSubscription = this.commonService.onWireframeCopyPasteOperation.subscribe((res) => {
      if (res.isPaste) {
        if (
          !this.commonService.WireframeClipboard ||
          !this.commonService.WireframeClipboard.nodes
        )
          return;

        this.commonService.showLoader();
        //do paste
        if (
          !this.commonService.BodyJson.rows[this.row].cols[this.col]
            .wireFrameData
        ) {
          //@ts-ignore
          this.commonService.BodyJson.rows[this.row].cols[
            this.col
            //@ts-ignore
          ].wireFrameData = { nodeDataArray: [], linkDataArray: [] };
        }

        let wireFrameData = this.commonService.BodyJson.rows[this.row].cols[
          this.col
        ].wireFrameData;

        if (wireFrameData && wireFrameData.nodeDataArray) {
          wireFrameData.nodeDataArray = wireFrameData.nodeDataArray.concat(
            this.commonService.WireframeClipboard.nodes
          );
        } else {
          wireFrameData.nodeDataArray = this.commonService.WireframeClipboard.nodes;
        }

        if (wireFrameData && wireFrameData["linkDataArray"]) {
          wireFrameData["linkDataArray"] = wireFrameData[
            "linkDataArray"
          ].concat(this.commonService.WireframeClipboard.links);
        } else {
          wireFrameData[
            "linkDataArray"
          ] = this.commonService.WireframeClipboard.links;
        }

        // for (var i = 0; i < wireFrameData["linkDataArray"].length; i++) {
        //   if (
        //     wireFrameData["linkDataArray"][i].from &&
        //     wireFrameData["linkDataArray"][i].to
        //   ) {
        //     delete wireFrameData["linkDataArray"][i].points;
        //   }
        // }

        this.diagram.clear();
        //this.diagram.model = new go.GraphLinksModel(this.wireframeData.draggableNodeDataArray, this.wireframeData.draggableLinkDataArray);
        setTimeout(() => {
          this.makeDraggableChart();
          this.commonService.hideLoader();
          setTimeout(() => {
            this.updateWireframeData(null);
          }, 2000);
        }, 2200);
      } else {
        //do copy
        this.commonService.WireframeClipboard = {
          nodes: this.selectedNodes,
          links: this.selectedLinks,
        };
      }
    });

    this.pasteClipWireframeSubscription = this.commonService.pasteClipboardData.subscribe((res) => {
      if (res.isWireframeFound) {
        this.diagram.clear();
        //this.diagram.model = new go.GraphLinksModel(this.wireframeData.draggableNodeDataArray, this.wireframeData.draggableLinkDataArray);
        setTimeout(() => {
          this.makeDraggableChart();
          this.commonService.hideLoader();
          setTimeout(() => {
            this.updateWireframeData(null);
          }, 2000);
        }, 2200);
      }
    });
  }

  ngOnChanges(changes: SimpleChange) {
    if (changes["row"] || changes["col"]) {
      this.diagram.clear();
      //this.diagram.model = new go.GraphLinksModel(this.wireframeData.draggableNodeDataArray, this.wireframeData.draggableLinkDataArray);
      setTimeout(() => {
        this.firstUpdateSkip = environment.wireframe_block_Update_Counter;
        this.makeDraggableChart();
      }, 2200);
    }

    setTimeout(() => {
      let canvas = this.diagramRef.nativeElement
        .children[0] as HTMLCanvasElement;
      canvas.style["z-index"] = this.zIndex;
      $(".diagramDiv > div").css("z-index", this.zIndex - 1);
      if (this.pointerEvent) {
        canvas.style["pointer-events"] = "none";
      } else {
        canvas.style["pointer-events"] = "all";
      }
    }, 2000);
  }

  makeDraggableChart() {
    //console.log('copied data', this.commonService.WireframeClipboard);
    let wireFrameData = this.commonService.BodyJson.rows[this.row].cols[
      this.col
    ].wireFrameData;
    console.log('wireFrameData', wireFrameData);
    if (wireFrameData && wireFrameData.nodeDataArray) {
      for (var i = 0; i < wireFrameData.nodeDataArray.length; i++) {
        wireFrameData.nodeDataArray[i].dloc = this.recalculatePositions(
          wireFrameData.nodeDataArray[i].screenWidth,
          wireFrameData.nodeDataArray[i].screenHeight,
          wireFrameData.nodeDataArray[i].dloc
        );
        if (wireFrameData.nodeDataArray[i].size)
          wireFrameData.nodeDataArray[i].size = this.recalculatePositions(
            wireFrameData.nodeDataArray[i].screenWidth,
            wireFrameData.nodeDataArray[i].screenHeight,
            wireFrameData.nodeDataArray[i].size
          );
        wireFrameData.nodeDataArray[i].screenWidth = document.getElementById(
          "assessmentCarousel"
        ).offsetWidth;
        wireFrameData.nodeDataArray[i].screenHeight = document.getElementById(
          "assessmentCarousel"
        ).offsetHeight;
      }
      if (wireFrameData && wireFrameData["linkDataArray"]) {
        for (var i = 0; i < wireFrameData["linkDataArray"].length; i++) {
          if (wireFrameData["linkDataArray"][i].points) {
            for (var j = 0; j < wireFrameData["linkDataArray"][i].points.length; j++) {
              // console.log('points toArray', wireFrameData["linkDataArray"][i].points._dataArray);
              wireFrameData["linkDataArray"][i].points[j] = this.recalculateLinkPositions(
                wireFrameData["linkDataArray"][i].screenWidth ? wireFrameData["linkDataArray"][i].screenWidth : null,
                wireFrameData["linkDataArray"][i].screenHeight ? wireFrameData["linkDataArray"][i].screenHeight : null,
                wireFrameData["linkDataArray"][i].points[j],
                j
              );
            }
            wireFrameData["linkDataArray"][i].screenWidth = document.getElementById(
              "assessmentCarousel"
            ).offsetWidth;
            wireFrameData["linkDataArray"][i].screenHeight = document.getElementById(
              "assessmentCarousel"
            ).offsetHeight;
          }
        }
      }
      this.diagram.model = go.Model.fromJson(wireFrameData);
    } else {
      this.diagram.model = new go.GraphLinksModel();
    }

    let pos = this.diagram.model.modelData.position;
    if (pos) {
      let newPos = this.recalculatePositions(
        this.diagram.model.modelData.screenWidth,
        this.diagram.model.modelData.screenHeight,
        this.diagram.model.modelData.position
      );
      this.diagram.initialPosition = go.Point.parse(newPos);
    }
  }

  updateWireframeData(e: any) {
    if (this.firstUpdateSkip == 0) {
      // console.log(e, e.model.nodeDataArray);
      // this.wireframeData.draggableNodeDataArray = e.model.nodeDataArray;
      // this.wireframeData.draggableLinkDataArray = e.model.linkDataArray;

      this.diagram.model.modelData = {
        position: go.Point.stringify(this.diagram.position),
        screenWidth: document.getElementById("assessmentCarousel").offsetWidth,
        screenHeight: document.getElementById("assessmentCarousel")
          .offsetHeight,
        modifiedBy: window["_spPageContextInfo"]
          ? window["_spPageContextInfo"].userDisplayName
          : "",
        modifiedDate: new Date().toISOString(),
      };

      let wireframe = JSON.parse(this.diagram.model.toJson());
      // for (var i = 0; i < wireframe.linkDataArray.length; i++) {
      //   if (wireframe.linkDataArray[i].from && wireframe.linkDataArray[i].to) {
      //     // delete wireframe.linkDataArray[i].points;
      //   }
      // }
      this.commonService.BodyJson.rows[this.row].cols[
        this.col
      ].wireFrameData = wireframe;
      console.log(
        "updated wireframe",
        this.commonService.BodyJson.rows[this.row].cols[this.col].wireFrameData
      );
      // this.modelChanged.emit(this.wireframeData);
    } else {
      this.firstUpdateSkip--;
    }
  }

  setScreenHeightWidthOnNode(e: go.DiagramEvent) {
    const obj = e.diagram.selection.first();
    if (obj instanceof go.Node) {
      obj.data["screenWidth"] = document.getElementById(
        "assessmentCarousel"
      ).offsetWidth;
      obj.data["screenHeight"] = document.getElementById(
        "assessmentCarousel"
      ).offsetHeight;
    } else if (obj instanceof go.Link) {
      obj.data["screenWidth"] = document.getElementById(
        "assessmentCarousel"
      ).offsetWidth;
      obj.data["screenHeight"] = document.getElementById(
        "assessmentCarousel"
      ).offsetHeight;
    }
  }

  onBgClick() {
    this.onBackgroundClick.emit();
  }

  recalculatePositions(width, height, pos) {
    let oldWidth = width ? width : 1477;
    let oldHeight = height ? height : 727;
    let currentWidth = document.getElementById("assessmentCarousel")
      .offsetWidth;
    let currentHeight = document.getElementById("assessmentCarousel")
      .offsetHeight;
    let oldPosX = parseFloat(pos.split(" ")[0]);
    let oldPosY = parseFloat(pos.split(" ")[1]);
    let newPosX = (currentWidth * oldPosX) / oldWidth;
    let newPosY = (currentHeight * oldPosY) / oldHeight;

    return newPosX + " " + newPosY;
  }

  recalculateLinkPositions(width, height, pos, axis) {
    let oldWidth = width ? width : 1477;
    let oldHeight = height ? height : 727;
    let currentWidth = document.getElementById("assessmentCarousel")
      .offsetWidth;
    let currentHeight = document.getElementById("assessmentCarousel")
      .offsetHeight;
    if (axis % 2 == 0) { // x axis
      let oldPosX = parseFloat(pos);
      let newPosX = (currentWidth * oldPosX) / oldWidth;
      return newPosX;
    }
    else { // y axis
      let oldPosY = parseFloat(pos);
      let newPosY = (currentHeight * oldPosY) / oldHeight;
      return newPosY;
    }
  }

  makePort(name, spot, output, input) {
    const $ = go.GraphObject.make;
    // the port is basically just a small transparent square
    return $(go.Shape, "Circle", {
      fill: null, // not seen, by default; set to a translucent gray by showSmallPorts, defined below
      stroke: null,
      desiredSize: new go.Size(7, 7),
      alignment: spot, // align the port on the main Shape
      alignmentFocus: spot, // just inside the Shape
      portId: name, // declare this object to be a "port"
      fromSpot: spot,
      toSpot: spot, // declare where links may connect at this port
      fromLinkable: output,
      toLinkable: input, // declare whether the user may draw links to/from here
      cursor: "pointer", // show a different cursor to indicate potential link point
    });
  }

  showSmallPorts(node, show) {
    node.ports.each((port) => {
      if (port.portId !== "") {
        // don't change the default port, which is the big shape
        port.fill = show ? "rgba(0,0,0,.3)" : null;
      }
    });
  }

  changeColor(e, obj) {
    let contextMenu = obj.part;
    this.nodeObject = contextMenu.data;
    console.log("curent color", this.nodeColor);
    const modalRef = this.modalService.open(this.colorPickerRef);
    modalRef.result
      .then((res) => {
        if (res == "success") {
          this.diagram.model.startTransaction("changed color");
          this.diagram.model.setDataProperty(
            this.nodeObject,
            "fill",
            this.nodeColor
          );
          this.diagram.model.commitTransaction("changed color");
        }
      })
      .catch((err) => {
        console.log(err);
      });
  }

  changeFont(e, obj) {
    let contextMenu = obj.part;
    this.nodeObject = contextMenu.data;
    console.log("curent node text color", this.nodeTextColor);
    console.log("this.nodeObject", this.nodeObject);
    if (this.nodeObject.font) {
      let splitArr = this.nodeObject.font.split(" ");
      console.log("splitArr", splitArr);
      this.nodeTextSize = splitArr[1].split("pt")[0];
      this.nodeTextStyle = splitArr[0];
      this.nodeTextFamily = splitArr[2];
    } else {
      this.nodeTextSize = 11;
      this.nodeTextStyle = "bold";
      this.nodeTextFamily = "Helvetica";
    }
    if (this.nodeObject.stroke) {
      this.nodeTextColor = this.nodeObject.stroke;
    } else {
      this.nodeTextColor = "#000000";
    }
    const modalRef = this.modalService.open(this.changeNodeTextRef);
    modalRef.result
      .then((res) => {
        if (res == "success") {
          let fontObj: string =
            this.nodeTextStyle +
            " " +
            this.nodeTextSize +
            "pt" +
            " " +
            this.nodeTextFamily;
          console.log("fontObj", fontObj);
          this.diagram.model.startTransaction("changed node text");
          this.diagram.model.setDataProperty(
            this.nodeObject,
            "stroke",
            this.nodeTextColor
          );
          this.diagram.model.setDataProperty(this.nodeObject, "font", fontObj);
          this.diagram.model.commitTransaction("changed node text");
        }
      })
      .catch((err) => {
        console.log(err);
      });
  }

  onItemSelect(e: any) {
    console.log("selected", e.subject);
    this.selectedNodes = [];
    this.selectedLinks = [];
    var it = e.subject.iterator;
    while (it.next()) {
      var part = it.value;
      if (part instanceof go.Node) {
        this.selectedNodes.push(part.data);
      } else if (part instanceof go.Link) {
        this.selectedLinks.push(part.data);
      }
    }
    console.log("selected nodes", this.selectedNodes);
    console.log("selected links", this.selectedLinks);
  }

  ngOnDestroy() {
    this.nodeBlinkSubscription.unsubscribe();
    this.pasteClipWireframeSubscription.unsubscribe();
    this.copyPasteWireframeSubscription.unsubscribe();
  }
}

Ah, I see that you construct a GraphLinksModel but you do not set those two properties. You have to initialize your models correctly before you save them.

How can i initialize two properties? Because it saying that property does not exist in model.

The type of Diagram.model is Model.
Either set it on the GraphLinksModel that you constructed, or cast the value of this.diagram.model.

Can you please give me example for the same?

You are seeing a compiler error, so naturally you need to change your code. In this case there is actually nothing wrong that happens at run-time, because the value of the model will always be an instance of GraphLinksModel because you just set it that way.

const model = new go.GraphLinksModel();
model.linkFromPortIdProperty = "fromPort";
model.linkToPortIdProperty = "toPort";
model.nodeDataArray = ...
model.linkDataArray = ...
this.diagram.model = model;
1 Like

Thanks Walter for your guidance its working as expected.