Search code examples
backbone.jsmarionettecollectionviewjointjs

Making JointJs & Backbone/Marionette work with collections (HTML items inside)


Let me know if you can help me out somehow, i'm kind of struggling to get my head around.

Starting with some Marionette application logics:

app.js

//basic setup
this.Graph = new joint.dia.Graph;
this.Paper = new joint.dia.Paper({ width: 640, height: 480, model: this.Graph });

// [...] lots of code

//adding elements
app.Elements.add(element);

So far so good. Now the tricky part. I want a collection.

JointCollectionView.js

module.exports = Marionette.CollectionView.extend({
    tagName: 'div',
    className: 'row',
    childView: JointView,

    addChild: function(child, ChildView, index){
        //does that make sense?
        app.Graph.addCell(child);

        //should i add it to collection?
        if (child.shouldBeShown()) {
            return Marionette.CollectionView.prototype.addChild.call(this,   child, ChildView, index);
        }
    },

    getChildView: function(item) {
        return app.Graph.getCell(item);
    }

    //[...]
})

Now even more tricky. How do i handle the joint-view to make it work with collections and also display html elements?

JointView.js

module.exports = joint.dia.ElementView.extend({ /* ?!?!?! */ });

//OR ?

module.exports = Marionette.ItemView.extend({
    jointElementView: null, //this will be like above somewhere else...

    initialize: function(options){
        jointElementView = new JointElementView({ /* ... */ });
    }
})

Solution

  • With the help of @seebiscuit i looked much deeper into jointjs and narrowed down how i should approach this problem (You didn't seem to be interested in the points though, so i'll answer myself)

    The following files were edited:

    app.js (changed, important!)

    //this step is necessary to create these element before the paper is created.
    //Make jointjs GLOBAL!!!!!!
    joint.shapes.html = {};
    joint.shapes.html.Element = require('views/Element');  //this dude im gonna use to create the elements 
    joint.shapes.html.ElementView = require('views/ElementView');  //this badboy will fire when i create those elements. MAGIC!
    
    //basic setup
    this.Graph = new joint.dia.Graph;
    this.Paper = new joint.dia.Paper({ width: 640, height: 480, model:     this.Graph });
    
    // [...] lots of code
    
    //adding elements
    app.Elements.add(element);
    

    JointCollectionView.js (beauti-/simplyfied)

    module.exports = Marionette.CollectionView.extend({
        tagName: 'div',
        className: 'row',
        childView: JointView,
    
        onRender: function(){
          // jointjs' paper is added here long after jointjs custom element init.
          this.el.appendChild(app.Paper.el);
        },
    
        onDestroy: function(){
          // removal of the paper is done here
          this.el.removeChild(app.Paper.el);
        },
    
        buildChildView: function(child, JointView, childViewOptions){
          // add model, jointjs' paper and graph into JointView's options
          var options = _.extend({model: child}, childViewOptions);
              options = _.extend({paper: app.Paper, graph: app.Graph}, options);
    
          return new JointView(options);
        }    
    
        //[...]
    })
    

    JointView.js (magic here!)

    module.exports = Marionette.ItemView.extend({
        tagName: 'div',
        className: 'html-element',
        template: "#agentTmpl",
    
        // store here just in case
        cell: null,
    
        // [...]
    
        initialize: function() {
          // initialize joinjs element-shape here
          Marionette.bindEntityEvents(this, this.model, this.modelEvents);
    
          if(this.cell == null){
            //notice `el: this.el` This will actually pass the option el to ElementView. Surprised? 
            //Yeah me too. From there i can do with the dom-element whatever i want
            this.cell = new joint.shapes.html.Element({ el: this.el, position: { x: 80, y: 80 }, size: { width: 250 } });
          }
        },
    
        onRender: function(){
              // after rendering add cell to graph
              this.options.graph.addCell(this.cell);
        },
    
        onDestroy: function(){
              // after removal remove cell from graph
              this.cell.remove();
        }
    });
    

    Element.js ElementView.js

    For simplicity more or less like here: http://www.jointjs.com/tutorial/html-elements To summarize what actually happens is: whenever a new Element is created ElementView will fire all necessary event (initialize, render & whatnot). From there you can manipulate the drawn svg elements or overlap (similar to the tutorial) with my previously created JointView's html. I basically put my JointView dom-element over the SVG which is drawn by jointjs.

    There you go. Fixed!