When changing the model for a view, is it better t

2019-03-11 06:25发布

问题:

I have an email view in my Backbone app. It's currently instantiated in the view action of my controller. It goes a little like this:

routes: {
  'email/:id': email
},

//...

email: function (id) {
  var email = new Email({
    id: id
  });
  this.emailView = new EmailView({
    model: email
  });
  email.fetch();
}

Now, the problem is, if I visit one email, then another, I end up creating two separate EmailViews. This means that, for example, the delete link in the EmailView is bound to two separate Email models, so clicking delete will delete both (not a good thing).

I'm looking at two solutions. In one, I'd cache the EmailView, and update its model. The problem then is that I'd have to re-bind the events in EmailView.

The other solution would be to create a new EmailView as I am at the moment, but unbind the old EmailView.el's events before replacing it.

Am I going about this the right way? Is there a better way to handle this situation? Cheers in advance.

回答1:

Create a separate view instance for each model instance. Each time you visit a new email then throw away the the old view and create a new view with the new email instance.

Probably what you have if I am guessing is a list view on the left hand side and an editor on the right. You select the email from the list on the left and you want the email body to appear on the right.

You really want about 5 view classes.

PageView
    has_one EmailCollectionView on left
    has_one EmailEditorView on right

EmailCollectionView
    has_many EmailSummaryViews as vertical list

EmailEditorView
    has_one EmailView centered

When you click in the EmailCollectionView you trigger an event which is picked up by EmailEditorView which throws away it's old instance of EmailView and renders a new version of EmailView

Something like that anyway



回答2:

We were originally creating new view objects every time someone would navigate to e.g. "candidates/show", just like the email example. That view bound a handler to the 'reset' event of its persistent model. When we then reset that model from the command line, we would see as many events fire as there were instances of that view. In other words, that view was not being garbage collected, even though the elements it added to the DOM were all completely destroyed.

Our solution was to just be sure to instantiate our top level views once, and then have them make their child views in their initialize methods. Then just re-render them as needed. Then you don't have to worry about the complexity of garbage collection (which, as far as I can tell from our experiments, doesn't happen anyway).



回答3:

I think that the DOM event handlers should be removed by a call to the view's remove() method - see http://api.jquery.com/remove (which Backbone calls under the covers)

If you override the remove() method to also remove binding to any model events as JohnnyO suggested, garbage collection should take care of removing the view.

I ended up doing overriding the remove() method to handle this:

class EmailView extends Backbone.View
    initialize: () ->
        @model.bind('change', @render)
    render: () =>
        # do some stuff
    remove: () ->
        @model.unbind('change', @render)
        super()

You can then use as such in a Router:

    routes:
      'email/:id': email

    //...

    email: (id) ->
      var email = new Email({
        id: id
      });
      this.emailView.remove() if this.emailView 
      this.emailView = new EmailView({
        model: email
      });
      email.fetch();
    }

This will work assuming that all events in the view are bound to the correct element - that is, that you're using @el (or this.el) in the View and that each view has its own @el/this.el.



标签: backbone.js