简体   繁体   English

链接文本的goJS下拉列表

[英]goJS dropdown for link text

I have simple python flask app where I send JSON data to my HTML and with goJS I display my graph which looks like this: 我有一个简单的python flask应用程序,可在其中将JSON数据发送到HTML并使用goJS显示如下图:

在此处输入图片说明

I want to make custom choices dropdown for users to edit node and link text. 我想对用户进行自定义选择下拉菜单,以编辑节点和链接文本。 So far, I used this code to make nodes text selectable in dropdown list: 到目前为止,我已使用此代码在下拉列表中使节点文本可选:

<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>State Chart</title>
<meta name="description" content="A finite state machine chart with editable and interactive features." />
<meta charset="UTF-8">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<link href="http://getbootstrap.com/dist/css/bootstrap.min.css" rel="stylesheet">

<script src="{{url_for('static', filename='go.js')}}"></script>

<!-- custom text editors -->
<script src="{{url_for('static', filename='TextEditorSelectBox.js')}}"></script>
<script src="{{url_for('static', filename='TextEditorRadioButtons.js')}}"></script>


<script src="{{url_for('static', filename='DataInspector.js')}}"></script>

<link href="https://gojs.net/latest/extensions/DataInspector.css" rel="stylesheet">
<link href="{{url_for('static', filename='DataInspector.css')}}" rel="stylesheet">


<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"> 
<script id="code">

    var nodeChoices = ['choice 1', 'choice 2', 'choice 3', 'choice 4', 'choice 5'];
    var linkChoices = ['link choice 1', 'link choice 2', 'link choice 3', 'link choice 4', 'link choice 5'];

    function init() {

        var $ = go.GraphObject.make;
        myDiagram =
        $(go.Diagram, "myDiagramDiv",  // must name or refer to the DIV HTML element
        {
            // start everything in the middle of the viewport
            initialContentAlignment: go.Spot.Center,
            // have mouse wheel events zoom in and out instead of scroll up and down
            "toolManager.mouseWheelBehavior": go.ToolManager.WheelZoom,
            // support double-click in background creating a new node
            "clickCreatingTool.archetypeNodeData": { text: "new node" },
            // enable undo & redo
            "textEditingTool.defaultTextEditor": window.TextEditorSelectBox,
            "undoManager.isEnabled": true,
            "layout": new go.ForceDirectedLayout()
        });


        // 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("textEdited", function(e) {
            console.log("Text is edited");
            console.log(e);

            //CHECK IF LINK,
            //IF YES REMOVE THAT OPTION FROM LIST

        });

        myDiagram.addDiagramListener("SelectionDeleting", function(e) {
            console.log("inside SelectionDeleting");
            console.log(e);

            //CHECK IF LINK,
            //IF YES PUT THAT OPTION BACK IN OPTION LIST

        });

            // define the Node template
        myDiagram.nodeTemplate =
          $(go.Node, "Auto",
            new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
            // define the node's outer shape, which will surround the TextBlock
            $(go.Shape, "RoundedRectangle",
              {
                parameter1: 20,  // the corner has a large radius
                fill: $(go.Brush, "Linear", { 0: "rgb(254, 201, 0)", 1: "rgb(254, 162, 0)" }),
                stroke: null,
                portId: "",  // this Shape is the Node's port, not the whole Node
                fromLinkable: true, fromLinkableDuplicates: true,
                toLinkable: true, toLinkableDuplicates: true,
                cursor: "pointer"
              }),
            $(go.TextBlock,
              {
                font: "bold 11pt helvetica, bold arial, sans-serif",
                editable: true,  // editing the text automatically updates the model data
                //textEditor: window.TextEditorRadioButtons, // defined in textEditorRadioButtons.js
                // this specific TextBlock has its own choices:
                textEditor: window.TextEditorRadioButtons,
                //choices: JSON.parse('{{ choices | tojson | safe}}')
                choices: nodeChoices
              },
            new go.Binding("text").makeTwoWay())
        );

        myDiagram.nodeTemplate.selectionAdornmentTemplate =
        $(go.Adornment, "Spot",
            $(go.Panel, "Auto",
            $(go.Shape, { stroke: "dodgerblue", strokeWidth: 2, fill: null }),
            $(go.Placeholder)
        ),
        $(go.Panel, "Horizontal",
            { alignment: go.Spot.Top, alignmentFocus: go.Spot.Bottom },
            $("Button",
                { click: editText },  // defined below, to support editing the text of the node
                $(go.TextBlock, "t",
                { font: "bold 10pt sans-serif", desiredSize: new go.Size(15, 15), textAlign: "center" })
            ),
            $("Button",
            { // drawLink is defined below, to support interactively drawing new links
                click: drawLink,  // click on Button and then click on target node
                actionMove: drawLink  // drag from Button to the target node
            },
            $(go.Shape,
                { geometryString: "M0 0 L8 0 8 12 14 12 M12 10 L14 12 12 14" })
            ),
            $("Button",
            {
                actionMove: dragNewNode,  // defined below, to support dragging from the button
                _dragData: { text: "?????", color: "lightgray" },  // node data to copy
                click: clickNewNode  // defined below, to support a click on the button
            },
            $(go.Shape,
                { geometryString: "M0 0 L3 0 3 10 6 10 x F1 M6 6 L14 6 14 14 6 14z", fill: "gray" })
          )
        )
      );

    function editText(e, button) {
        //console.log(e);

      var node = button.part.adornedPart;
      console.log("node");
      //console.log(node);
      e.diagram.commandHandler.editTextBlock(node.findObject("TEXTBLOCK"));
      //$("#nodeText").val(node.findObject("TEXTBLOCK"));
    }

    function drawLink(e, button) {
      var node = button.part.adornedPart;
      var tool = e.diagram.toolManager.linkingTool;
      tool.startObject = node.port;
      e.diagram.currentTool = tool;
      tool.doActivate();
    }

   // used by both clickNewNode and dragNewNode to create a node and a link
    // from a given node to the new node
    function createNodeAndLink(data, fromnode) {
        var diagram = fromnode.diagram;
        var model = diagram.model;
        var nodedata = model.copyNodeData(data);
        model.addNodeData(nodedata);
        var newnode = diagram.findNodeForData(nodedata);
        var linkdata = model.copyLinkData({});
        model.setFromKeyForLinkData(linkdata, model.getKeyForNodeData(fromnode.data));
        model.setToKeyForLinkData(linkdata, model.getKeyForNodeData(newnode.data));
        model.addLinkData(linkdata);
        diagram.select(newnode);
        return newnode;
    }

    // the Button.click event handler, called when the user clicks the "N" button
    function clickNewNode(e, button) {
        var data = button._dragData;
        if (!data) return;
        e.diagram.startTransaction("Create Node and Link");
        var fromnode = button.part.adornedPart;
        var newnode = createNodeAndLink(button._dragData, fromnode);
        newnode.location = new go.Point(fromnode.location.x + 200, fromnode.location.y);
        e.diagram.commitTransaction("Create Node and Link");
    }

    // the Button.actionMove event handler, called when the user drags within the "N" button
    function dragNewNode(e, button) {
        var tool = e.diagram.toolManager.draggingTool;
        if (tool.isBeyondDragSize()) {
            var data = button._dragData;
            if (!data) return;
            e.diagram.startTransaction("button drag");  // see doDeactivate, below
            var newnode = createNodeAndLink(data, button.part.adornedPart);
            newnode.location = e.diagram.lastInput.documentPoint;
            // don't commitTransaction here, but in tool.doDeactivate, after drag operation finished
            // set tool.currentPart to a selected movable Part and then activate the DraggingTool
            tool.currentPart = newnode;
            e.diagram.currentTool = tool;
            tool.doActivate();
        }
    }

    // using dragNewNode also requires modifying the standard DraggingTool so that it
    // only calls commitTransaction when dragNewNode started a "button drag" transaction;
    // do this by overriding DraggingTool.doDeactivate:
    var tool = myDiagram.toolManager.draggingTool;
    tool.doDeactivate = function() {
        // commit "button drag" transaction, if it is ongoing; see dragNewNode, above
        if (tool.diagram.undoManager.nestedTransactionNames.elt(0) === "button drag") {
            tool.diagram.commitTransaction();
        }
        go.DraggingTool.prototype.doDeactivate.call(tool);  // call the base method
    };

    // replace the default Link template in the linkTemplateMap
    myDiagram.linkTemplate =
        $(go.Link,  // the whole link panel
        {
            curve: go.Link.Bezier, 
            adjusting: go.Link.Stretch,
            reshapable: true, 
            relinkableFrom: true, 
            relinkableTo: true,
            toShortLength: 3
        },
        new go.Binding("points").makeTwoWay(),
        new go.Binding("curviness"),
        $(go.Shape,  // the link shape
            { strokeWidth: 1.5 }),
        $(go.Shape,  // the arrowhead
            { toArrow: "standard", stroke: null }),
        $(go.Panel, "Auto",
            $(go.Shape,  // the label background, which becomes transparent around the edges
            {
                fill: $(go.Brush, "Radial", { 0: "rgb(240, 240, 240)", 0.3: "rgb(240, 240, 240)", 1: "rgba(240, 240, 240, 0)" }),
                stroke: null
            }),
            $(go.TextBlock,  // the label text
            {
                textAlign: "center",
                font: "12pt helvetica, arial, sans-serif",
                margin: 4,
                editable: true,  // enable in-place editing
                textEditor: window.TextEditorRadioButtons,
                //choices: JSON.parse('{{ choices | tojson | safe}}')
                choices: linkChoices
            },
            // editing the text automatically updates the model data
            new go.Binding("text").makeTwoWay())
        )
    );


    var inspector = new Inspector('myInspectorDiv', myDiagram,
    {
        // uncomment this line to only inspect the named properties below instead of all properties on each object:
        // includesOwnProperties: false,
        properties: {
            "text": { },
            // an example of specifying the type
            "password": { show: Inspector.showIfPresent, type: 'password' },
            // key would be automatically added for nodes, but we want to declare it read-only also:
            "key": { readOnly: true, show: Inspector.showIfPresent },
            // color would be automatically added for nodes, but we want to declare it a color also:
            "color": { show: Inspector.showIfPresent, type: 'color' },
            // Comments and LinkComments are not in any node or link data (yet), so we add them here:
            "Comments": { show: Inspector.showIfNode  },
            "flag": { show: Inspector.showIfNode, type: 'checkbox' },
            "LinkComments": { show: Inspector.showIfLink },
            "isGroup": { readOnly: true, show: Inspector.showIfPresent }
        }
    });

    // read in the JSON data from flask
    loadGraphData();

    }

    function loadGraphData() {
        var graphDataString = JSON.parse('{{ diagramData | tojson | safe}}');
        console.log("graphDataString");
        console.log(graphDataString);

        myDiagram.model = go.Model.fromJson(graphDataString);
    }

    function saveGraphData(form, event) {
        console.log("inside saveGraphData");
        event.preventDefault();

        document.getElementById("mySavedModel").value = myDiagram.model.toJson();
        form.submit();
    }

    function zoomToFit(){
        console.log("inside zoomToFit");
        myDiagram.zoomToRect(myDiagram.documentBounds);
    }

    function zoomIn(){
        console.log("inside zoomIn");
        myDiagram.commandHandler.increaseZoom();
    }
    function zoomOut(){
        console.log("inside zoomOut");
        myDiagram.commandHandler.decreaseZoom();
    }

</script>
</head>
<body onload="init()">

    <div id=formWrapper style="padding: 30px;">

        <form method="POST" action="http://localhost:5000/updateResultFile" name="updateResultFileForm" 
        id="updateResultFileForm" 
        onsubmit="saveGraphData(this, event);">

            <div id="graphWrapper" style="margin-bottom: 15px;">
                <div id="myDiagramDiv" style="border: solid 1px black; width: 100%; height: 800px;margin-bottom: 15px;"></div>
                <div style="display: none;"><input id="mySavedModel" name="mySavedModel"></div>

                <button class="btn btn-default" type="submit"> Save <i class="fa fa-save"> </i> </button>

            </div>

        </form>

        <div id="myInspectorDiv">
        </div>

        <div>
            <button class="btn btn-default" onclick="zoomToFit()"> Zoom to fit  <i class="fa fa-search"> </i> </button>
            <button class="btn btn-default" onclick="zoomIn()"> Zoom in  <i class="fa fa-search-plus"> </i> </button>
            <button class="btn btn-default" onclick="zoomOut()"> Zoom out  <i class="fa fa-search-minus"> </i> </button>
        </div>


    </div>

</body>
</html>

And It looks like this: 看起来像这样:

在此处输入图片说明

It's all fine with node text, but I have problem with link text. 节点文本很好,但是链接文本有问题。 I want when user selects one option that he can not use that option anymore on other link texts. 我希望当用户选择一个选项时,他不能再在其他链接文本上使用该选项。 Also I want to make when that link is deleted that that option is available again. 我也想使该链接删除后,该选项再次可用。

As you can see in code, I added 2 event listeners on my diagram (textEdited and SelectionDeleting) and they work fine when user is editing text or deleting something, but I don't know how to extract information about event object and its text. 正如您在代码中看到的那样,我在图表上添加了2个事件侦听器(textEdited和SelectionDeleting),当用户编辑文本或删除某些内容时它们可以正常工作,但是我不知道如何提取有关事件对象及其文本的信息。

I need to make sure it is link so I can remove or add that event object text in my choices list. 我需要确保它是链接,以便可以在我的选择列表中删除或添加该事件对象文本。 Any help will be appreciated. 任何帮助将不胜感激。

OK, let's assume that the list of choices for the link labels is held in the Model.modelData object. 好的,我们假设链接标签的选择列表保存在Model.modelData对象中。 I'll name the property "choices", but of course you can use whatever name you like. 我将属性命名为“ choices”,但是您当然可以使用任何喜欢的名称。 myDiagram.model.set(myDiagram.model.modelData, "choices", ["one", "two", "three"]);

Your Link template might look something like: myDiagram.linkTemplate = $(go.Link, $(go.Shape), $(go.Shape, { toArrow: "OpenTriangle" }), $(go.TextBlock, { background: "white", editable: true, textEditor: window.TextEditorSelectBox, // defined in extensions/textEditorSelectBox.js textEdited: function(tb, oldstr, newstr) { var choices = tb.diagram.model.modelData.choices; var idx = choices.indexOf(newstr); if (idx >= 0 && oldstr !== newstr) { console.log("removing choice " + idx + ": " + newstr); var newchoices = Array.prototype.slice.call(choices); newchoices.splice(idx, 1); tb.diagram.model.set(tb.diagram.model.modelData, "choices", newchoices); tb.editable = false; // don't allow choice again } } }, new go.Binding("text"), new go.Binding("choices").ofModel()) ); 您的Link模板可能类似于: myDiagram.linkTemplate = $(go.Link, $(go.Shape), $(go.Shape, { toArrow: "OpenTriangle" }), $(go.TextBlock, { background: "white", editable: true, textEditor: window.TextEditorSelectBox, // defined in extensions/textEditorSelectBox.js textEdited: function(tb, oldstr, newstr) { var choices = tb.diagram.model.modelData.choices; var idx = choices.indexOf(newstr); if (idx >= 0 && oldstr !== newstr) { console.log("removing choice " + idx + ": " + newstr); var newchoices = Array.prototype.slice.call(choices); newchoices.splice(idx, 1); tb.diagram.model.set(tb.diagram.model.modelData, "choices", newchoices); tb.editable = false; // don't allow choice again } } }, new go.Binding("text"), new go.Binding("choices").ofModel()) );

Note how the TextBlock.textEditor is defined to be a TextEditorSelectBox and the TextBlock.textEdited event handler is defined to set the modelData.choices property to be a new Array without the chosen string. 请注意,如何将TextBlock.textEditor定义为TextEditorSelectBox以及如何将TextBlock.textEdited事件处理程序定义为将modelData.choices属性设置为没有选定字符串的新Array。

It also sets TextBlock.editable back to false so that the user cannot re-choose for that Link. 它还会将TextBlock.editable设置回false,以使用户无法为该Link重新选择。 That's one way to avoid problems with repeated edits; 这是避免重复编辑出现问题的一种方法。 but you could implement your own policies. 但您可以实施自己的政策。 In retrospect I think the more likely policy would be to add the old value to and remove the new value from the modelData.choices Array. 回想一下,我认为更可能的策略是将旧值添加到modelData.choices数组中并modelData.choices删除新值。

Also, you'll want to implement a Model Changed listener that notices when Links have been removed from the model, so that you can add its choice back to the myDiagram.model.modelData.choices Array. 另外,您将希望实现一个更改模型的侦听器,该侦听器会通知何时从模型中删除了链接,以便可以将其选择添加回myDiagram.model.modelData.choices数组。 In your Diagram initialization: $(go.Diagram, . . ., { "ModelChanged": function(e) { if (e.change === go.ChangedEvent.Remove && e.modelChange === "linkDataArray") { var linkdata = e.oldValue; var oldstr = linkdata.text; if (!oldstr) return; var choices = e.model.modelData.choices; var idx = choices.indexOf(oldstr); if (idx < 0) { console.log("adding choice: " + oldstr); var newchoices = Array.prototype.slice.call(choices); newchoices.push(oldstr); e.model.set(e.model.modelData, "choices", newchoices); } } } }) 在您的图表初始化中: $(go.Diagram, . . ., { "ModelChanged": function(e) { if (e.change === go.ChangedEvent.Remove && e.modelChange === "linkDataArray") { var linkdata = e.oldValue; var oldstr = linkdata.text; if (!oldstr) return; var choices = e.model.modelData.choices; var idx = choices.indexOf(oldstr); if (idx < 0) { console.log("adding choice: " + oldstr); var newchoices = Array.prototype.slice.call(choices); newchoices.push(oldstr); e.model.set(e.model.modelData, "choices", newchoices); } } } })

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM