使用 GoJS 创建端口的问题

Issue in creating ports with GoJS

我正在测试 Javascript 的 GoJS 库。 我希望在组的两侧创建端口。 我附上了程序的可执行文件。

当我从调色板创建一个新对象时,右键单击该对象,然后 select "Add left port",调试消息显示变量 "arr" 已声明 "undefined" 在执行时。 (程序第 111 和 112 行)。

感谢您的帮助。

<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Dynamic Ports</title>
<meta name="description" content="Nodes with varying lists of ports on each of four sides." />
<!-- Copyright 1998-2017 by Northwoods Software Corporation. -->
<meta charset="UTF-8">
<script src="../release/go-debug.js"></script>
<script src="../assets/js/goSamples.js"></script>  <!-- this is only for the GoJS Samples framework -->
<!-- LL <script>goCode("diagramEvents", 600, 200)</script>-->
<script src="/Users/leonlevy/gojs/site/extensionsTS/ResizeMultipleScript.js"></script>
<span id="diagramEventsMsg" style="color: red"></span>
<span id="changeMethodsMsg" style="color: red"></span>
<span id="BackgroundDoubleClicked" style="color: red"></span>
<p>
<script id="code">
  function init() {
    if (window.goSamples) goSamples();  // init for these samples -- you don't need to call this
    var $ = go.GraphObject.make;  //for conciseness in defining node templates
    myDiagram =
      $(go.Diagram, "myDiagramDiv",
        {
          allowDrop: true, // from Palette
          // what to do when a drag-drop occurs in the Diagram's background
          mouseDrop: function(e) { finishDrop(e, null); },
      //   layout:  null, // Diagram has no layout
/*            $(go.GridLayout,
              { wrappingWidth: Infinity, alignment: go.GridLayout.Position, cellSize: new go.Size(1, 1) }),*/
          initialContentAlignment: go.Spot.Center,
          "commandHandler.archetypeGroupData": { isGroup: true, category: "OfGroups" },
          "undoManager.isEnabled": true
        });
        // LL
        function showMessage(s) {
          document.getElementById("diagramEventsMsg").textContent = s;
        }
    // when the document is modified, add a "*" to the title and enable the "Save" button
    myDiagram.addDiagramListener("Modified", function(e) {
      var button = document.getElementById("SaveButton");
      if (button) button.disabled = !myDiagram.isModified;
      var idx = document.title.indexOf("*");
      if (myDiagram.isModified) {
        if (idx < 0) document.title += "*";
      } else {
        if (idx >= 0) document.title = document.title.substr(0, idx);
      }
    });

myDiagram.addDiagramListener("ObjectSingleClicked",
    function(e) {
      var part = e.subject.part;
      var heightPart = part.actualBounds.height;
      var widthPart = part.actualBounds.width;
      var xPart = part.actualBounds.position.x;
      var yPart = part.actualBounds.position.y;
      var xMouseClic = myDiagram.lastInput.documentPoint.x;
      var yMouseClic = myDiagram.lastInput.documentPoint.y;
      var xPartMiddle = xPart+ widthPart/2;
      var side = xMouseClic<= xPartMiddle ? "Left" : "Right";
       if (!(part instanceof go.Link)) {console.log("hello leon");}
    });
    myDiagram.addDiagramListener("BackgroundSingleClicked",
        function(e) {
                    showMessage("Clicked on background " + myDiagram.lastInput.documentPoint);
                                          //console.log("hello leon");}
        });


myDiagram.addDiagramListener("BackgroundDoubleClicked", // LL KO
    function(e) {
                  showMessage("Double-clicked at " );//+ e.myDiagram.lastInput.documentPoint);
                  //showMessage("Clicked on " + myDiagram.lastInput.documentPoint);
    });

myDiagram.addDiagramListener("ClipboardPasted",
    function(e) { showMessage("Pasted " + e.myDiagram.selection.count + " parts"); });
// LL end

    // To simplify this code we define a function for creating a context menu button:
    function makeButton(text, action, visiblePredicate) {
      return $("ContextMenuButton",
               $(go.TextBlock, text),
               { click: action },
              //{showMessage( "button: " + myDiagram.lastInput.button);
               // don't bother with binding GraphObject.visible if there's no predicate
               visiblePredicate ? new go.Binding("visible", "", function(o, e) { return o.myDiagram ? visiblePredicate(o, e) : false; }).ofObject() : {});
              //}
            }
    var nodeMenu =  // context menu for each Node
      $(go.Adornment, "Horizontal",
                  makeButton("Add left port",
                             function (e, obj) { addPort("left"); }),
                  makeButton("Add right port",
                             function (e, obj) { addPort("right"); }),

      ); //Leon end nodeM

        // Add a port to the specified side of the selected nodes.
        function addPort(side) {
          myDiagram.startTransaction("addPort");
          myDiagram.selection.each(function(node) {
            // skip any selected Links
            if (!(node instanceof go.Node)) return;
            // compute the next available index number for the side
            var i = 0;
            while (node.findPort(side + i.toString()) !== node) i++;
            // now this new port name is unique within the whole Node because of the side prefix
            var name = side + i.toString();
            // get the Array of port data to be modified
            var arr = node.data[side + "Array"];
            showMessage ("node: " + node + "; name: " + name + ";arr: " + arr + ";node.data: " + node.data);
            if (arr) {
              // create a new port data object
              var newportdata = {
                portId: name,
                portColor: go.Brush.randomColor()
                // if you add port data properties here, you should copy them in copyPortData above
              };
              // and add it to the Array of port data
              myDiagram.model.insertArrayItem(arr, -1, newportdata);
            }
          });
          myDiagram.commitTransaction("addPort");
        }

    var portSize = new go.Size(8, 8);
    // this function is used to highlight a Group that the selection may be dropped into
    function highlightGroup(e, grp, show) {
      if (!grp) return; showMessage("ok")
      e.handled = true;
      if (show) {
        // cannot depend on the grp.diagram.selection in the case of external drag-and-drops;
        // instead depend on the DraggingTool.draggedParts or .copiedParts
        var tool = grp.diagram.toolManager.draggingTool;
        var map = tool.draggedParts || tool.copiedParts;  // this is a Map
        // now we can check to see if the Group will accept membership of the dragged Parts
        if  (grp.canAddMembers(map.toKeySet())) {
          grp.isHighlighted = true;
          return;
        }
      }
      grp.isHighlighted = false;
    }
    // Upon a drop onto a Group, we try to add the selection as members of the Group.
    // Upon a drop onto the background, or onto a top-level Node, make selection top-level.
    // If this is OK, we're done; otherwise we cancel the operation to rollback everything.
    function finishDrop(e, grp) {
      var ok = (grp !== null
                ? grp.addMembers(grp.diagram.selection, true)
                : e.diagram.commandHandler.addTopLevelParts(e.diagram.selection, true));
      if (!ok) e.diagram.currentTool.doCancel();
    }
    var portMenu =  // context menu for each port
      $(go.Adornment, "Vertical",
        makeButton("Remove port",
                   // in the click event handler, the obj.part is the Adornment;
                   // its adornedObject is the port
                   function (e, obj) { removePort(obj.part.adornedObject); }),
        makeButton("Change color",
                   function (e, obj) { changeColor(obj.part.adornedObject); }),
        makeButton("Remove side ports",
                   function (e, obj) { removeAll(obj.part.adornedObject); })
      );

            // support double-clicking in the background to add a copy of this data as a node
            myDiagram.toolManager.clickCreatingTool.archetypeNodeData = {
              name: "Unit",
              leftArray: [],
              rightArray: [],
              topArray: [],
              bottomArray: []
            };

      myDiagram.groupTemplateMap.add("OfGroups",
        $(go.Group, "Table",
        { locationObjectName: "BODY",
          locationSpot: go.Spot.Center,
          selectionObjectName: "BODY",
          contextMenu: nodeMenu
        },
          {
            background: "transparent",
            resizable: true,
            // highlight when dragging into the Group
            mouseDragEnter: function(e, grp, prev) { highlightGroup(e, grp, true); },
            mouseDragLeave: function(e, grp, next) { highlightGroup(e, grp, false); },
            //computesBoundsAfterDrag: true,
            mouseDrop: finishDrop,
            resizable: true, resizeObjectName: "SHAPE"
            //, minSize: new go.Size(36, 46)
          },
        new go.Binding("background", "isHighlighted", function(h) { return h ? "rgba(0,0,255,0.)" : "transparent"; }).ofObject(),
          // save the modified size in the model node data
        new go.Binding("desiredSize", "size", go.Size.parse).makeTwoWay(go.Size.stringify),
        new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
        $(go.Shape, "Rectangle",
          { fill: null, stroke: "rgba(0,50,255,1)", strokeWidth: 3, name: "SHAPE"}),
        $(go.Panel, "Vertical",  {alignment: go.Spot.Top},// title above Placeholder
          $(go.Panel, "Horizontal",  // button next to TextBlock
            { stretch: go.GraphObject.Horizontal, background: "rgba(0,0,255,0.1)" },
            $("SubGraphExpanderButton",
              { alignment: go.Spot.Right, margin: 5 }),
            $(go.TextBlock,
              { //text: "verticalAlignment: Top", verticalAlignment: go.Spot.Top,
                alignment: go.Spot.Left,
                editable: true,
                margin: 5,
                font: "bold 18px sans-serif",
                opacity: 0.75,
                stroke: "#404040"
              },
              new go.Binding("text", "text").makeTwoWay())
          ),  // end Horizontal Panel
          $(go.Placeholder,
            { padding: 5, alignment: go.Spot.TopLeft })
        ),  // end Vertical Panel
        // the Panel holding the left port elements, which are themselves Panels,
        // created for each item in the itemArray, bound to data.leftArray
        $(go.Panel, "Vertical",
          new go.Binding("itemArray", "leftArray"),
          { row: 1, column: 0,
            itemTemplate:
              $(go.Panel,
                { _side: "left",  // internal property to make it easier to tell which side it's on
                  fromSpot: go.Spot.Left, toSpot: go.Spot.Left,
                  fromLinkable: true, toLinkable: true, cursor: "pointer",
                  contextMenu: portMenu },
                new go.Binding("portId", "portId"),
                $(go.Shape, "Rectangle",
                  { stroke: null, strokeWidth: 0,
                    desiredSize: portSize,
                    margin: new go.Margin(1,0) },
                  new go.Binding("fill", "portColor"))
              )  // end itemTemplate
          }
        ),  // end Vertical Panel
        // the Panel holding the right port elements, which are themselves Panels,
        // created for each item in the itemArray, bound to data.rightArray
        $(go.Panel, "Vertical",
          new go.Binding("itemArray", "rightArray"),
          { row: 1, column: 2,
            itemTemplate:
              $(go.Panel,
                { _side: "right",
                  fromSpot: go.Spot.Right, toSpot: go.Spot.Right,
                  fromLinkable: true, toLinkable: true, cursor: "pointer",
                  contextMenu: portMenu },
                new go.Binding("portId", "portId"),
                $(go.Shape, "Rectangle",
                  { stroke: null, strokeWidth: 0,
                    desiredSize: portSize,
                    margin: new go.Margin(1, 0) },
                  new go.Binding("fill", "portColor"))
              )  // end itemTemplate
          }
        ),  // end Vertical Panel
        )
     );  // end groupTemplateMap.add("OfGroups"
      // initialize the Palette and its contents
      myPalette =
        $(go.Palette, "myPaletteDiv",
          {
            groupTemplateMap: myDiagram.groupTemplateMap,
            layout: $(go.GridLayout, { wrappingColumn: 1, alignment: go.GridLayout.Position })
          });

          myPalette.nodeTemplate =
          $(go.Node, "Horizontal",
            $(go.Shape,
              { width: 10, height: 10, fill: "white" },
              new go.Binding("fill", "color")),
            $(go.TextBlock,
              new go.Binding("text", "color"))
          );

          myPalette.model = new go.GraphLinksModel([
            { key: 101, text: 'UNIT', isGroup: true, isSubProcess: true,
                category: "OfGroups", isAdHoc: true,  loc: '0 0' },

          ]);

        }
</script>
</head>
<body onload="init()">
<!-- LL -->
<body onload="goIntro()">
<div id="container" class="container-fluid">
<div id="content">
<div id="sample">
  <div style="width: 100%; display: flex; justify-content: space-between">
      <div id="myPaletteDiv" style="width: 100px; margin-right: 2px; background-color: whitesmoke; border: solid 1px black"></div>
      <div id="myDiagramDiv" style="flex-grow: 1; height: 480px; border: solid 1px black"></div>
    </div>
  <p>
    See the <a href="../intro/ports.html">Ports Intro page</a> for an explanation of GoJS ports.
  </p>
    <textarea id="mySavedModel" style="width:100%;height:250px">
    </textarea>
  </div>
</div>
</body>
</html>

您需要确保节点绑定了正确的数据。特别是,数据对象应该有一个 leftArray 属性 ,它是一个包含端口描述符对象的数组。 (而且它似乎也期望 rightArray 属性。)您可以通过查看源 属性 名称来确定每个 Node 的预期属性节点模板中的每个 Binding

您可以通过查看 Panel.itemTemplate 在其数据 [=16] 中使用的属性来确定任一数据数组中每个端口描述符的预期属性=]绑定s.