Search code examples
javascriptbackbone.jsreactjsjsplumb

How to delete a JSPlumb-Element in a React / Backbone environment?


I have a backbonejs model-collection containing models for JSPlumb nodes (ID, parentIDs). The DOM representation is handled by React (divs with Endpoints). If I delete a model from the collection:

nodeCollection.remove(aNodeModel)

it gets removed from the DOM with all containing components - nice.

If I now add a new node, the Endpoint is not added. I tracked this issue down to some internals in JSPlumb: If I do a

jsPlumb.remove(aNodeModel.id)

the Endpoint-problem vanishes but I get a React "Invariant Violation" because the aNodeModel was not removed from the nodeCollection.

If I remove the jsPlumb Endpoint manually before I remove the model from the collection:

jsPlumb.removeAllEndpoints(aNodeModel.id)

the problem remains.

This feels somehow like a deadlock... Any suggestions?


Solution

  • Open Source roxx.

    I solved this problem by adding a parameter to the jsPlumb .remove() method controlling the actual DOM-deletion of the element inside jsPlumb. If I set this to false jsPlumb gets cleaned up nicely an I can let React delete the actual DOM-element after that:

    jsPlumb.remove(sourceNodeID, false, [false]) 
    

    This are the changes I did to the jsPlumb source in dom.jsPlumb-1.7.5.js, lines 5176 and 5210.

    var _doRemove = function(info, affectedElements, removeDOMElement) {
      _currentInstance.removeAllEndpoints(info.id, true, affectedElements);
      var _one = function(_info) {
        _currentInstance.getDragManager().elementRemoved(_info.id);
        _currentInstance.anchorManager.clearFor(_info.id);
        _currentInstance.anchorManager.removeFloatingConnection(_info.id);
        delete _currentInstance.floatingConnections[_info.id];
        delete managedElements[_info.id];
        delete offsets[_info.id];
    
        var actuallyRemoveDOMElement = true;            
        if (removeDOMElement.length > 0) {
          actuallyRemoveDOMElement = removeDOMElement[0];
        }
    
        if (_info.el) {
          if (actuallyRemoveDOMElement) {
            _currentInstance.removeElement(_info.el);
          }
          _info.el._jsPlumb = null;
        }
      };
    
      // remove all affected child elements
      for (var ae = 1; ae < affectedElements.length; ae++) {
        _one(affectedElements[ae]);
      }
      // and always remove the requested one from the dom.
      _one(info);
    };
    
    
    /**
     * Remove the given element, including cleaning up all endpoints registered for it.
     * This is exposed in the public API but also used internally by jsPlumb when removing the
     * element associated with a connection drag.
     */
    this.remove = function (el, doNotRepaint, removeDOMElement) {
      var info = _info(el), affectedElements = [];
      if (info.text) {
        info.el.parentNode.removeChild(info.el);
      }
      else if (info.id) {
        _currentInstance.batch(function () {
          _doRemove(info, affectedElements, removeDOMElement);
        }, doNotRepaint === false);
      }
      return _currentInstance;
    };