justspamjustin / BossView

Manage your Marionette.js views like a boss! Manages events and rendering of sub-views.
http://justspamjustin.github.io/BossView/
MIT License
48 stars 6 forks source link

Add an this.insertView (or equivalent) for dynamic subviews. #3

Closed abritinthebay closed 10 years ago

abritinthebay commented 10 years ago

Right now I'm reduced to the following for dynamic updating

_.each(dynamicListOfViews, function (itemData, name) {
    this.options.subViews[name] = function () {
        var model = new MyModel(itemData);
        return new MyView({model: model});
    };
}, this);
this._initializeSubViews();
this._initializeChildViewEvents();
this._initializeSubViewEventBubbling();

This is obviously a bit rough.

A simple insertView/addSubView method could takethe view (or object for a list of views) and then do the needed setup.

Would create much cleaner code when dealing with dynamic subviews (that may not be directly from a collection for example).

Sure I could make a contrived collection and use some form of CollectionView but then I end up writing a whole lot of other boilerplate code that doesn't really solve the issue and I end up not at an ideal solution anyhow :(

sam3k commented 10 years ago

Any update on this? I've been using your plugin and now find myself needing to render subviews dynamically when the fetch data arrives.

abritinthebay commented 10 years ago

I ended up going with the bossview+collectionview solution I mentioned above which was actually pretty clean after a little bit of a refactor.

That said - this would still be extremely useful feature for other issues.

sam3k commented 10 years ago

Hmm, in my case i have three views, on i prefetch before i add to regions. The other two i have to figure out a way to render the view IF the data comes through and there are no 500s. Any suggestions?

abritinthebay commented 10 years ago

Well given that the views can be independent of the model - maybe just work event based?

Like... have the view check if there is data in the model or if it sync'd and then do a remove/hide if it errors out? Or just have it hidden until you know it synced?

abritinthebay commented 10 years ago

The subview code can be refactored in this case so I may end up just submitting a pull request to add InsertSubView as a method

sam3k commented 10 years ago

Just to give you an idea, this is how i was fetching from two apis:

$.when(billingModel.fetch(), membershipModel.fetch()).done(function () {
    model = new Model({ billing: billingModel, membership: membershipModel });
    view  = new View({ model: model });

    Core.addRegions({ mainRegion: Core.el });
    Core.mainRegion.show(view);
}).fail(function(){
    console.log('either billingModel or memberShipModel did not fetch correctly!');
});

The problem is, sometimes the billingModel will fail with 503 or similar but I still need to render the membershipModel which will always work. Here is how the master (boss) view look like:

var View = Marionette.BossView.extend({

        subViews: {
            heading: HeadingView,
            membership: MembershipView,
            balance: BalanceView,
            personal: PersonalView
        }

    });

of the views above, i would like to not render "membership" and "personal" if the billingModel fetch fails with 503 or similar.

abritinthebay commented 10 years ago

Sounds like an event driven solution would be best for you for sure. Hell... you could ONLY render the membership and personal on success then.

So you just need a backbone event emitter, and that could be the parent view - or whatever works for your system.

Then when the billingModel is rendered it just triggers an event on that emitter and the subviews then render automatically on that event. Until then they simply don't render.

This would require overriding the render function on those views, but it's a pretty trivial function at that point.

sam3k commented 10 years ago

That sounds exactly like the solution that I need but how do i add new views to bossview in the future and also, how do i specify the order?

abritinthebay commented 10 years ago

Well in the case I'm talking about - you just add them as normal and let the views themselves dictate what happens. They can call this.remove() if they aren't needed anymore.

But yes - this feature needs to be added.

sam3k commented 10 years ago

Gotcha! Thanks for the clarification. Okay, for reference:

var OptionalView = Marionette.ItemView.extend({
    template: _.template(''),
    initialize: function () {
      var that = this;

      this.model = new MyModel();

      this.model.fetch()
        .done(function() { 
            that.template = _.template('NEW TEMPLATE <%= boo %>'),
            that.render(); 
        })
        .fail(function() {
            that.remove();
        });
    }
});
abritinthebay commented 10 years ago

I was thinking more a case of

this.listenTo(model, "sync", successCallback);
this.listenTo(model, "error", failCallback);

as I'm not sure about how promises/deferreds are handled in Backbone. Also it's a cleaner read.

justspamjustin commented 10 years ago

I pushed to a branch called insertsubview. Please try my updated Marionette.BossView.js: https://raw.githubusercontent.com/justspamjustin/BossView/insertsubview/Marionette.BossView.js

Bossview now has two new functions: initializeSubView(subViewName, subViewFunction) The subViewName is the equivalent of what would be the name of the key in your subViews object. And subViewFunction is the equivalent of what would be the value in your subViews object. There is also: renderSubView(subViewName) pass the name of the subView to render at any time in the views lifecycle. Try it out and let me know. If it works for you, I'll merge it into master.

abritinthebay commented 10 years ago

This seems to work great. Would love you to merge this in :) :+1:

justspamjustin commented 10 years ago

Merged in and is v0.1.4.

agwidarsito commented 9 years ago

Haha, great work for this :-)