Binding a Backbone Model to a Marionette ItemView - blocking .fetch()?

≯℡__Kan透↙ 提交于 2019-11-29 22:39:33
Derick Bailey

From a very basic standpoint, throwing aside the specific example that you've provided, here is how I would approach the problem and solution.

A Generic Problem / Solution

Here's a generic version of the problem:

  • You need to fetch a model by its id.
  • You need a view to render after the model has been fetched.

This is fairly simple. Attach the model to the view before fetching the data, then use the "sync" event of the model to render the view:

MyView = Backbone.View.extend({
  initialize: function(){
    this.model.on("sync", this.render, this);
  },

  render: function(){ ... }
});


myModel = new MyModel({id: someId});
new MyView({
  model: myModel
});

myModel.fetch();

Things to note:

I'm setting up the model with its id, and the view with the model before calling fetch on the model. This is needed in order to prevent a race condition between loading the data and rendering the view.

I've specified generic Backbone stuff here. Marionette will generally work the same, but do the rendering for you.

Your Specific Needs

Blocking Fetch

Bad idea, all around. Don't try it.

A blocking fetch will make your application completely unresponsive until the data has returned from the server. This will manifest itself as an application that performs poorly and freezes any time the user tries to do anything.

The key to not doing this is taking advantage of events and ensuring that your events are configured before you actually make the asynchronous call, as shown in my generic example.

And don't call the fetch from within the model's initializer. That's asking for trouble as you won't be able to set up any views or events before the fetch happens. I'm pretty sure this will solve the majority of the problems you're having with the asynchronous call.

Events Between View And Model

First, I would avoid using MyApp.vent to communicate between the model and the view instance. The view already has a reference to the model, so they should communicate directly with each other.

In other words, the model should directly trigger the event and the view should listen to the event on the model. This works in the same way as my simple example, but you can have your model trigger any event you want at any time.

I would also be sure to the use bindTo feature of Marionette's views, to assist in cleaning up the events when the view is closed.

MyView = Backbone.Marionette.ItemView.extend({
  initialize: function(){
    this.bindTo(this.model, "do:something", this.render, this);
  }
});

MyModel = Backbone.Model.extend({
  doSomething: function(){
    this.trigger('do:something');
  }
});

myModel = new MyModel();
new MyView({
  model: myModel
});

myModel.doSomething();

Other Items

There are some other items that I think are causing some problems, or leading toward odd situations that will cause problems.

For example, you have too much happening in the DOMReady event: $ ->

It's not that you have too much code being executed from this event, but you have too much code defined within this event. You should not have to do anything more than this:

$ -> 
  App.MyApp.start(data)

Don't define your Marionette.Application object in this event callback, either. This should be defined on its own, so that you can set up your initializers outside of the DOMReady callback, and then trigger them with the app.start() call.

Take a look at the BBCloneMail sample application for an example on rendering a layout and then populating its regions after loading data and external templates:

source: https://github.com/derickbailey/bbclonemail

live app: http://bbclonemail.heroku.com/


I don't think I'm directly answering your questions the way you might want, but the ideas that I'm presenting should lead you to the answer that you need. I hope it helps at least. :)

See Derick's new suggestion to tackle this common problem at: https://github.com/marionettejs/backbone.marionette/blob/master/upgradeGuide.md#marionetteasync-is-no-longer-supported

In short, move the asynchronous code away from your views, which means you need to provide them with models whose data has already been fetched. From the example in Marionette's upgrade guide:

 Marionette.Controller.extend({
  showById: function(id){
    var model = new MyModel({
      id: id
    });

    var promise = model.fetch();

    $.when(promise).then(_.bind(this.showIt, this));
  },

  showIt: function(model){
    var view = new MyView({
      model: model
    });

    MyApp.myRegion.show(view);
  }
});
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!