Save and Load jsPlumb flowchart including exact anchors and connections
Asked Answered
I

1

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?

Iphagenia answered 16/12, 2013 at 20:55 Comment(0)
C
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.

Charlena answered 2/1, 2014 at 23:16 Comment(12)
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.Iphagenia
How come when you save & load, unattached duplicates appear as well?Merimerida
@Iphagenia 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.Antibiotic
This isn't working for me. Did you get the above getConnections() to work?Pleading
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 plsMecham
It would be awesome if there were a nice jsPlumb.jsonify() function, but this is the next best thing.Krafftebing
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] ]];Tubbs
For me it is showing this error: "Cannot read property 'id' of null" in anchors:elem.anchors. Help me what I need to do.Ranunculaceous
Hi Greg can you please check this thread ?#56914343Sexennial
Hi @user9964622—Just seen this. I see you got your answer on this Gihub thread: github.com/jsplumb/jsplumb/issues/860. Great you got it working. You should update your question on SO with the resolution.Charlena
I'm also unable to see an edit with paint & style!? Can someone point me into the right direction please?Damaging
@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.Charlena

© 2022 - 2024 — McMap. All rights reserved.