I'm currently working with a CompositeView. The problem is, when I call destroy() on one of the child views from the CollectionView part, it still remains in the this.children._view obejct, even do it disappears from the screen.
If I try to do anything with it (let's say call destroy() again) than it says, it has already been destroyed, but than why doesn't it get deleted from the object?
Thanks in advance.
Child views are not parent aware
This may sound annoying, but it's set up like that by design, since a CollectionView child may also exists on its own. As far as I can't tell children of a Collection View are not aware that they are children (except for the case that the model is destroyed [and thus removed from permanent storage]).
CollectionView.removeChildView can help
However, Marionette does expose a CollectionView method that is made exactly for what you want to do, CollectionView.removeChildView(view).
CollectionView.removeChildView will remove the view from the DOM, destroy the view (remove any listeners on the view) and remove it from the children container. Just pass in one of your views.
If you need some tips on how to trigger CollectionView.removeChildView on an event on your child view, drop me a comment.
Related
I am building a SPA and am trying to figure out a safe and clean way to delete all backbone entities when navigating away from one section of the application. I am aware of the Model.destroy(), View.remove() and Collection.reset() methods. My main concerns are:
Model.destroy() takes care of destroying the model on the server. Does one still need to manually delete the Javascript model?
Similariy, I realize that View.remove() will remove the view from the DOM. How should one safely get rid of the view object?
Collection.reset() clears the models in the collection. Will this also delete the underlying Javascript model objects or do they need to be explicitly deleted?
How would one get rid of the collection object itself?
My questions may seem simple to some but they have been confusing me for a while. I have not found any useful information regarding this exact problem which is why I decided to post here. I am also a relatively new to Javascript and am unaware of Javascript's garbage collection scheme. Does Javascript have a garbage collector and will it take care of deleting all such objects?
I am also looking into understanding the best way to remove Views.
View.remove() only removes the view from the DOM. My guess is that one still needs to take care of removing/destroying the underlying Model and unbinding all event listeners. Is that correct?
I often use _.bindAll to change the context of a function call. I have not come across a way to unbind this binding. My understanding says that this is unnecessary. Am I right?
Here is a good resource for learning about garbage collection in javascript:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Management
To summarize, the only thing you need to worry about with Backbone is removing custom event handlers in your view, and a common way to do this is to override remove and remove the handlers before calling Backbone.View.prototype.remove. You don't need to manually remove events that you declared in the events property of the view, if you are using that.
To be a little more specific, lets consider the three types of objects you are asking about in turn. If you are using the backbone router, then you are probably creating a view object in a router method, maybe assigning it to a var, and then setting the html somewhere on the page. When the user navigates away, a different router method is invoked, and the reference you created to this view is unreachable. It will get garbage collected assuming you didn't create it as a property on window or your root level object or something like that.
The remove method is about removing the view from the DOM. The garbage collector takes care of removing the view from memory. The remove method is a convenient place to put any cleanup code that you need to run before taking the view off the page, so unhook your custom event handlers here.
Likewise, model.destroy is not about destroying the model object stored in memory, its about sending an AJAX DELETE request to the server. Model objects are garbage collected like everything else, and will go away once they are unreachable. If the only references to a model are contained within a view, then removing that view will cause the model to be garbage collected. Same goes for collections.
And for your last point, the underscore bind will not be an issue. Its about binding context (the value of this that a function is invoked with), not about binding event handlers. Remove those event handlers as usual.
Model.destroy() takes care of destroying the model on the server. Does one still need to manually delete the Javascript model?
Yes, you still need to manually delete the model. Model.destroy() does not delete it.
Similariy, I realize that View.remove() will remove the view from the DOM. How should one safely get rid of the view object?
Remove all references to it. If nothing is pointing at your object it will get garbage collected. Generally speaking, unless you go out of your way to make sure references to an object stay alive (such as global variables) or get careless with .on() you should be ok.
Note that using .listenTo() on your views instead of .on() makes your life easier. Memory leaks with views used to be a big problem when we just had .on()(See here for more).
Collection.reset() clears the models in the collection. Will this also delete the underlying Javascript model objects or do they need to be explicitly deleted?
How would one get rid of the collection object itself?
Does Javascript have a garbage collector and will it take care of deleting all such objects?
As long as there's no remaining reference to your objects they will get garbage collected.
I already described the birds-eye view of this (make sure you're no longer referencing your objects!), but here is a pretty good article about garbage collection in the context of Backbone.
View.remove() only removes the view from the DOM. My guess is that one still needs to take care of removing/destroying the underlying Model and unbinding all event listeners. Is that correct?
Note that it also calls .stopListening(), which unbinds your listeners if you used .listenTo() in your views. You do not need to remove/destroy the underlying model to let it get GC'd, again as long as there are no remaining references to it. Using .listenTo() with a model is a reference to it, but if you .remove() your view that will remove that reference.
I often use _.bindAll to change the context of a function call. I have not come across a way to unbind this binding. My understanding says that this is unnecessary. Am I right?
It's not clear to me what you would be trying to achieve by "unbinding," so I would say that it isn't necessary.
When i call destroy on a marionette view and later try to use it throws an error saying it has been destroyed. Also when i remove the view from the DOM directly it also throws the same error. So i need to know how i can certain that the view is destroyed.
You can check whether a view has been destroyed or not using the view's isDestroyed property.
// Example
if (view.isDestroyed) {
view = new View({});
}
view.doSomething();
So #JonathanMiles is correct but I think some elaboration may help you and our Googlers.
You aptly mentioned two ways in which you may destroy a view.
Call this.destroy() on your view. The .destroy() method does several crucial things: 1. sets the view.isDestroyed bit so that Marionette can know that your view has been removed from the DOM; removes the view from the DOM and most bindings attached to it by calling view.remove() which eventually calls view.$el.remove(), and removes other bindings that view.$el.remove() does not (like Marionette ui elements and behaviors).
Simpy do this.$el.remove() on your view. This removes the HTML associated with the view from the DOM as well as events bound to that HTML, but not events that are created on cached HTML elements.
Now, with the first mentioned, it's obvious that Marionette will know that the view is destroyed. If you need to know whether the view is destroyed, like #JonathanMiles pointed out, simply obtain a reference to your view and check the value of view.isDestroyed.
But, if you decide to remove the view HTML from the DOM yourself, Marionette will not know if the view is not in the DOM. You may get errors, specifically since you may still have events bound to a view without HTML associated with it (although most of these situations would probably just pass silently unnoticed), but not from Marionette. Even worse, you may decide that to replace the rejected view, you'll spawn new views. The old view instance however will still live. While you won't have zombie events, this is a potential memory leak.
The moral of the story is don't simply do view.$el.remove(). Use Marionette's view.destroy() to completely free the view object for garbage collection, and reap the advantage of knowing if the view .isDestroyed.
I've created a Behavior which creates a CompositeView and the CompositeView then creates ItemViews from a collection. The ItemViews have a few events attached to them. These views trigger an event via the Application.vent event aggregator. I then listen for the event ("click:unlink") in the Controller which created the original view that contains the behavior.
But I think I'm starting to see zombie events, at least I assume they're zombie events. When I click the link that triggers the "click:unlink" event, the function that triggers the event I'm only seeing that called once, but I'm seeing the function defined in the listener fired multiple times.
So my question, when a view defines behaviors and those behaviors create their own views, When the original view is destroyed for whatever reason, do the views associated with the attached behavior also get destroyed? I added some onDestroy functions on the views created by the behavior, but I never see them called. Maybe I'm not understanding how behaviors are supposed to be used?
A behavior will not destroy any views it has created, the only way a view created by a behavior could be destroyed is if it was placed in a region which was either emptied or replaced with another view.
I am optimizing my application. Originally, it's a Ext.TabPanel but I decided to use only a Ext.TabBar docked at the bottom and change the views above, so it requires a lot of add/remove actions from my main Ext.Container.
So my question is: in what way I should do to add/remove items from my Ext.Container effectively? I mean: fast, not cause memory-leaks, and also, not cause error like this: the view with a button in it, firstly added, all handlers (which are define through refs and control in a controller) work well but the second time (i.e it's removed and added again later), all handlers die.
Thanks in advance!
You have to ensure that you destroy the panel is destroyed otherwise it would be sitting in the dom.
Generally to remove a component from a container you use the Container remove() function which takes in the first parameter as the item to be removed and the second one is a boolean which instructs for it to be destroyed or not. You should ensure you set it to true to make sure you keep your DOM as lean as possible. Unless you're going to be reusing that component in the near future and do not want to render it again, then you do not need to destroy it.
http://docs.sencha.com/touch/2-0/#!/api/Ext.Container-method-remove
I have a parent view with parent model, and a child view.
I would like to pass a reference to part of the model (a collection) to the child model, so the child model can monitor this for changes and react accordingly.
I'm not sure about the best way to do this - possibly one of these approaches?
Pass whole model : I don't want to do this, as the model contains a
bunch of stuff that the child shouldn't know about.
Pass part of the model : I don't think this is possible... if I use
this.model.get('thesubpart'), I think I will be passing a value, not
a reference.
Bind the child event in the parent view : is this the way to go? I'm
not sure how I would go about doing this.
Your question is a little bit confusing, but I think you want a child model to react to something happening in a parent model, though I'm not sure if the parent is a collection or a single model itself.
The preferred way to do this is through events (i.e. parent.on('change', child.handleParentChange), where handleParentChange is a function defined on the child model. Since you seem to be interested in only a specific attribute change, you could bind to the more specific "change:thesubpart" event.
There are different ways to do this, such as an event aggregator, but the general idea is the same. Be careful of zombies, though. If the parent outlives the child, it will keep the child in memory because of the binding (an advantage of the event aggregator, if implemented correctly).