Search code examples
javascriptbackbone.jsmarionette

Render view on click event


Is there a way so that the ItemViews contained in a CompositeView only render on say clicking a button? I would like a change in the collection to update the Composite View's dom but each individual ItemView should not render until required.

Pardon me if I'm a little vague in my description but I have very limited knowledge of backbone and marionette.


Solution

  • As you well know, Marionette is eager to take your Composite (or Collection) View's children views and spawn them. That's why included in the Composite View render method is the _renderChildren process. Once invoked, there's really no way to selectively render children views.

    But there's a back door that gets around rendering your entire collection. It's a simple as initializing your Composite View with an empty collection, like this

    //Define MyCollection` and MyCompositieView and then...
    var myCollection = new MyCollection(); // Construct an empty collection
    
    var myCompositeView = new MyCompositeView({ collection: myCollection });
    

    An "empty" Composite View will render its own template normally, and simply skip _renderChildren.

    You can then wire an event to call myCompositeView.collection.add(model). You'll notice that Marionette listens for an add event on your collection,

    _initialEvents: function() {
      if (this.collection) {
        this.listenTo(this.collection, 'add', this._onCollectionAdd);
    
        // Other _initialEvents methods...
      }
    },
    

    and _onCollectionAdd is responsible for rendering the added model:

    _onCollectionAdd: function(child) {
      this.destroyEmptyView();
      var ChildView = this.getChildView(child);
      var index = this.collection.indexOf(child);
      this.addChild(child, ChildView, index);  // The rendering happens here
    },
    

    Putting it all together

    To make this work you'd have to have an array of your models inside your CompositeView, but outside that view's collection. I usually just wire up an $.getJSON (or any other AJAX method) to get the data and store it in a property of the View object. Say you do this on initialize:

    initialize: function() {
      var that = this,
          dataUrl = "some/url";
      $.getJSON(dataUrl, function(data) {
        that.myModels = data;
      });
    },
    

    And, in your Composite View you'd probably have an event, say a click on an element of your Composite view:

    events: {
      'click button': 'addChild'
    }
    
    addChild: function (event) {
      // functionality to identify which child to add to the collection
      this.collection.add(this.myModels[j]); // Where 'j' is the index the model you want lives in.
    });
    

    When addChild is called the collection adds the proper model, and Mariontte makes sure to render a child view populated with this model.

    There variations on how to do this, and you don't have to have an event wired in your view. But I think I proved how you can have methods render independently. If you provide more information I can give you more ideas.