22

This is the jsFiddle of the flowchart editor I am building.

This is an example of what can be easily created with "Add Decision" + "Add Task", connecting and moving the elements.

Example flowchart - user configured

Now for the hard part: I want to be able to save and load the exact flowchart. For this I got started based with a similar thread here at Stackoverflow.

For this I added the "Save" and "Load" buttons that export/import the flowchart to/from JSON (shown in a textform in the jsFiddle after save - same textform can be used for loading).

The save function:

function saveFlowchart(){
            var nodes = []
            $(".node").each(function (idx, elem) {
            var $elem = $(elem);
            var endpoints = jsPlumb.getEndpoints($elem.attr('id'));
            console.log('endpoints of '+$elem.attr('id'));
            console.log(endpoints);
                nodes.push({
                    blockId: $elem.attr('id'),
                    nodetype: $elem.attr('data-nodetype'),
                    positionX: parseInt($elem.css("left"), 10),
                    positionY: parseInt($elem.css("top"), 10)
                });
            });
            var connections = [];
            $.each(jsPlumb.getConnections(), function (idx, connection) {
                connections.push({
                    connectionId: connection.id,
                    pageSourceId: connection.sourceId,
                    pageTargetId: connection.targetId
                });
            });

            var flowChart = {};
            flowChart.nodes = nodes;
            flowChart.connections = connections;
            flowChart.numberOfElements = numberOfElements;

            var flowChartJson = JSON.stringify(flowChart);
            //console.log(flowChartJson);

            $('#jsonOutput').val(flowChartJson);
        }

The resulting JSON of the example above:

{"nodes":[{"blockId":"startpoint","nodetype":"startpoint","positionX":273,"positionY":8},{"blockId":"endpoint","nodetype":"endpoint","positionX":310,"positionY":385},{"blockId":"taskcontainer1","nodetype":"task","positionX":381,"positionY":208},{"blockId":"decisioncontainer2","nodetype":"decision","positionX":261,"positionY":103}],"connections":[{"connectionId":"con_18","pageSourceId":"decisioncontainer2","pageTargetId":"taskcontainer1"},{"connectionId":"con_25","pageSourceId":"taskcontainer1","pageTargetId":"endpoint"},{"connectionId":"con_32","pageSourceId":"decisioncontainer2","pageTargetId":"endpoint"},{"connectionId":"con_46","pageSourceId":"startpoint","pageTargetId":"decisioncontainer2"}],"numberOfElements":2}

With that I am able to save the position of the elements as well as part of the information of the connections. Here the load function:

function loadFlowchart(){
            var flowChartJson = $('#jsonOutput').val();
            var flowChart = JSON.parse(flowChartJson);
            var nodes = flowChart.nodes;
            $.each(nodes, function( index, elem ) {
                if(elem.nodetype === 'startpoint'){
                    repositionElement('startpoint', elem.positionX, elem.positionY);
                }else if(elem.nodetype === 'endpoint'){
                    repositionElement('endpoint', elem.positionX, elem.positionY);
                }else if(elem.nodetype === 'task'){
                    var id = addTask(elem.blockId);
                    repositionElement(id, elem.positionX, elem.positionY);
                }else if(elem.nodetype === 'decision'){
                    var id = addDecision(elem.blockId);
                    repositionElement(id, elem.positionX, elem.positionY);
                }else{

                }
            });

            var connections = flowChart.connections;
            $.each(connections, function( index, elem ) {
                 var connection1 = jsPlumb.connect({
                    source: elem.pageSourceId,
                    target: elem.pageTargetId,
                    anchors: ["BottomCenter", [0.75, 0, 0, -1]]

                });
            });

            numberOfElements = flowChart.numberOfElements;
        }

However, the exact position of the anchors and connections are lost. Same example again, the result after deleting the elements and then loading the exported JSON:

Flowchart after loading JSON

This comes not as a big surprise as I have not yet stored the information. But I am stuck at this point.

My question is: which information regarding the anchors/connectors position do I need to store for the whole flowchart design and how I can extract it from (& load into it again) jsPlumb?

Community
  • 1
  • 1
hbit
  • 959
  • 2
  • 13
  • 33

1 Answers1

26

See this JSFiddle for a solution.

You need to save the anchor details as follows. This conforms to the Anchor representation defined here. Note the double nesting to avoid the JQuery auto-flatten on the map.

    $.each(jsPlumb.getConnections(), function (idx, connection) {
      connections.push({
      connectionId: connection.id,
      pageSourceId: connection.sourceId,
      pageTargetId: connection.targetId,
      anchors: $.map(connection.endpoints, function(endpoint) {

        return [[endpoint.anchor.x, 
        endpoint.anchor.y, 
        endpoint.anchor.orientation[0], 
        endpoint.anchor.orientation[1],
        endpoint.anchor.offsets[0],
        endpoint.anchor.offsets[1]]];

      })
    });
  });

...and load them, as follows:

    $.each(connections, function( index, elem ) {
     var connection1 = jsPlumb.connect({
      source: elem.pageSourceId,
      target: elem.pageTargetId,
      anchors: elem.anchors
    });

  });

Note that this solution does not preserve end-point details including the shape and type of end-points. It only preserves the anchor details, as you requested.

MrNobody007
  • 1,827
  • 11
  • 32
Greg Ross
  • 3,479
  • 2
  • 27
  • 26
  • 1
    Thank you very much, works perfect. I accept this answer and did a quick edit here that includes the paint style of the endpoint so that the formatting is also preserved. – hbit Jan 03 '14 at 15:06
  • How come when you save & load, unattached duplicates appear as well? – radtek Jan 23 '14 at 22:21
  • @hbit I was unable to find the edit which you made for saving & restoring the paint & style of endpoint. Can you please share the code ? I tried, but I guess I'm missing something. – coding_idiot Apr 06 '14 at 06:22
  • This isn't working for me. Did you get the above getConnections() to work? – jallen Oct 01 '14 at 14:25
  • am having another problem after all this, the bind function is working with these loaded connections !!!! But bind is working with newly created connection. ??? help me pls – Nisfan Jul 22 '15 at 12:43
  • 1
    It would be awesome if there were a nice `jsPlumb.jsonify()` function, but this is the next best thing. – StockB Oct 22 '15 at 18:15
  • If you need to get the above working, replace return [[ endpoint.anchor.x, endpoint.anchor.y, endpoint.anchor.getOrientation()[0], endpoint.anchor.getOrientation()[1], endpoint.anchor.offsets[0], endpoint.anchor.offsets[1] ]]; – dalcam Jan 20 '16 at 03:10
  • For me it is showing this error: "Cannot read property 'id' of null" in anchors:elem.anchors. Help me what I need to do. – Darshan theerth Jan 08 '18 at 06:41
  • Hi Greg can you please check this thread ?https://stackoverflow.com/questions/56913843/cannot-establish-connection-source-does-not-exist – The Dead Man Jul 06 '19 at 13:01
  • Hi @user9964622—Just seen this. I see you got your answer on this Gihub thread: https://github.com/jsplumb/jsplumb/issues/860. Great you got it working. You should update your question on SO with the resolution. – Greg Ross Jul 08 '19 at 21:05
  • I'm also unable to see an edit with paint & style!? Can someone point me into the right direction please? – alexanderadam Jul 25 '19 at 12:33
  • @Wurstsalat, the changes were made in the JSFiddle by hbit. If you look at the edit and compare the new and old JSFiddles you'll see that hbit modified my original js code around the endpoints. – Greg Ross Aug 27 '19 at 19:49