event stays bound to zombie instance


I'm facing with what looks like a zombie reference case.

First off, this is the code I'm working with.

Here you'll find my User's details manager layout gist

The controller, instead, can be found here

The flow of operations is as follow:

  1. Admin access the list of users upon hitting the route that calls "manageUsers" on the controller.
  2. Admin clicks on a user row and the event "users:selected" gets triggered and the listener recevies the user instance to work with.
  3. DetailsLayout gets instantiated and rendered in the application's main layout. Specifically in its "content" region.
  4. The view that deals with user details form gets instantiated too and rendered inside DetailsLayout's "dettagli" region.
  5. At this point, the administrator is interested in managing this user's notes. So he clicks on the tab labeled "notes" and the list of notes gets rendered in the DetailsLayout "note" region.
  6. Clicking on the note the administrator wants to work with fires the event "notes:selected" which passes the note instance to the appropriate event handler (DetailsLayout.showNote).

At this point once the administrator clicks on the link that fires DetailsLayout.showUsers which, in turn fires the event "users:index" listened by the afore mentioned controller, he gets back to point 1 of the list above.

Repeating the whole process up to point 5 works as expected but, as soon as the administrator picks the note to work the debugger shows an error about the DetailsLayout region "note" not being defined.

Running through the code step by step I have found that on the second run the events fired are correctly bound to the proper instance of DetailsLayout except for the point 6 which is still bound to the instance used on first pass.

The code above shows that each and every time the user's details are shown a new instance of DetailsLayout gets created. Shouldn't this be enough to guarantee that no zombies are still around? If I recall correctly from backbone.marionette documentation, each time layout.region.show() gets hit, it closes the previous view (unbinding events and all the rest) and attaches the newly passwed view.

What's that I'm doing the wrong way?

Thanks in advance for your help. Regards


Backbone.marionette version is 0.8.4

Backbonejs version is 0.9.2

Your constructor in the UserDetailsList is the problem:

            constructor: (@user) ->
                Hub.vent.on("notes:selected",(note) => @showNote(note))
                Hub.vent.on("notes:show", => @showNotes())
                Hub.vent.on("notes:save", (note) => @saveNote(note))
                Hub.vent.on("notes:delete", (note) => @deleteNote(note))
                Hub.vent.on("notes:new", => @showNote(new Note()))

You're directly binding to the Hub.vent but never explictly unbinding from these events. Therefore, your instance of UserDetails list is hanging around in memory forever.

To fix this, you should use the layout's bindTo method which will track the binding and unbind it for you when the layout's close method is called.

            constructor: (@user) ->
                @bindTo(Hub.vent, "notes:selected",(note) => @showNote(note))
                @bindTo(Hub.vent, "notes:show", => @showNotes())
                @bindTo(Hub.vent, "notes:save", (note) => @saveNote(note))
                @bindTo(Hub.vent, "notes:delete", (note) => @deleteNote(note))
                @bindTo(Hub.vent, "notes:new", => @showNote(new Note()))

Be sure to correctly close your layout when you no longer need it. Call the close method on your layout instance. This will correctly clean up all of the bindTo events that the constructor set up.

FYI - a Layout extends directly from ItemView so all of the information / documentation about an ItemView also applies to a Layout, including the use of bindTo, close, etc.

This video can help you solving your question :)
By: admin