Both View
and CollectionView
are aware of their lifecycle state
which indicates if the view is rendered, attached to the DOM or destroyed.
Both View
and CollectionView
share methods for checking lifecycle state.
isRendered()
Returns a boolean value reflecting if the view is considered rendered.
isAttached()
Returns a boolean value reflecting if the view is considered attached to the DOM.
isDestroyed()
Returns a boolean value reflecting if the view has been destroyed.
Marionette Views are Backbone Views and so when they are instantiated the view
has an el
. That el
will be the root node for the view and other than its contents it
will not change for the life of the view unless directly manipulated (ie: view.$el.addClass
)
The view can be passed an existing el
either in the DOM (ie: el: $('.foo-selector')
)
or in memory (ie: el: $('<div></div>')
) or most commonly, the view constructs
its own el
at instantiation as documented on backbonejs.org.
Marionette will determine the initial state of the view as to whether the view is considered already rendered or attached. If a view is already rendered or attached its state will reflect that status, but the related events will not have fired.
For more information on instanting a view with pre-rendered DOM see: Prerendered Content.
setElement
Backbone.View
allows the user to change the view's el
after instantiaton using
setElement
. This method can be used in Marionette
as well, but should be done with caution. setElement
will redelegate view events, but it will
essentially ignore children of the view, whether through regions
or through children
and the
view's behaviors
will also be unaware of the change. It is likely better to reconstuct a new
view with the new el
than to try to change the el
of an existing view.
In Marionette rendering a view is changing a view's el
's contents.
What rendering indicates varies slightly between the two Marionette views.
Note Once a view is considered "rendered" it cannot be unrendered until it is destroyed.
View
RenderingFor View
, rendering entails serializing the view's data, passing it to a template,
and taking the results of that template and replacing the contents of the view's el
. So when a View
is
instantiated it is considered rendered if the el
node contains any content. However after instantiation
a template may render empty in which case the View
will still be considered "rendered" even though it
contains no content.
CollectionView
RenderingFor CollectionView
, rendering signifies that the view's
children
were created and attached to the
view's el
. So unlike View
a CollectionView
can be instantiated with content in its el
, but until
the children
are "rendered" the entire view is not considered rendered.
Notably if there are no children
when rendering, the view will still be considered rendered. This is
true whether or not an emptyView
is rendered.
So it is possible for a CollectionView
to be "rendered" but the el
to only be an empty tag.
Also note that just like View
a CollectionView
may have a template
which is rendered and attached to
the el
during the render
, but the template rendering itself has no bearing on the status of the CollectionView
.
Rendering child views is often best accomplish after the view render as typically the first render happens prior to the view entering the DOM. This helps to prevent unnecessary repaints and reflows by making the DOM insert at the highest possible view in the view tree.
The exception is views with prerendered content. In the case that the view is instantiated
rendered, child views are best managed in the view's initialize
.
View
ChildrenIn general the best method for adding a child view to a View
is to use showChildView
in the render
event.
View regions will be emptied on each render so views shown outside of the render
event will still need be reshown
on subsequent renders.
CollectionView
ChildrenThe primary use case for a CollectionView
is maintaining child views to match the state of a Backbone Collection.
By default children will be added or removed to match the models within the collection.
However a CollectionView
can have children in addition to, or instead of, views matching the collection
.
If you add a view to a CollectionView
s children by default it will treat it as any other view added from the collection
.
This means it is subject to the viewComparator
and
viewFilter
.
So if you are accounting for added views in your viewFilter
and viewComparator
the best place to add these children is
likely in the render
event as the views will only be added once
(or re-added if the children are rebuilt in a subsequent render
) and managed in the sort or filter as the collection
is updated.
Unlike managed children there may be cases where you want to insert views to the results of the CollectionView
after the
collection
changes, or after sorting and/or filtering. In these cases the solution might depend slightly on the features
used on the CollectionView
.
The goal will be to add the unmanaged views after other views are added and to remove any unmanaged views prior to any
managed children
changes. To do so you must understand which CollectionView
event
will occur prior to changes to the children
for your particular use case. By default a CollectionView
sorts according
to the collection
sort, so unless viewComparator
is disabled, the best event for removing unmanaged views is the
before:sort
event, but if viewComparator
is false the next event
to consider is the before:filter
event if your CollectionView
has
a viewFilter
, otherwise the before:render:children
event
is ideal.
Once you have determined the best strategy for removing your unmanaged child views, adding them is best handled in the
render:children
event. Additionally adding a child
with addChildView
will itself cause these events to occur, so to prevent stack overflows, it is best to use a flag to guard
the adds and to insert a new view at a specified index.
The following simplistic example will add an unmanaged view at the 5th index and remove it prior to any changes to the children
.
In a real world scenario it will likely be more complicated to keep track of which view to remove in the onBeforeSort
.
import { CollectionView } from 'backbone.marionette';
const MyCollectionView = CollectionView.extend({
childView: MyChildView,
onBeforeSort() {
this.removeChildView(this.children.findByIndex(5));
},
onRenderChildren() {
this.addFooView();
},
addFooView() {
if (this.addingFooView) {
return;
}
this.addingFooView = true;
this.addChildView(new FooView(), 5);
this.addingFooView = false;
}
});
In Marionette a view is attached if the view's el
can be found in the DOM.
The best time to add listeners to the view's el
is likely in the attach
event.
While the el
of the view can be attached the contents of the view can be removed and added to
during the lifetime of the view. If you are adding listeners to the contents of the view rather than
attach
the dom:refresh
event would be best.
The attached state is maintained when attaching a view with a Region
or as a child of a CollectionView
or during view instantiation.
If a view is attached by other means like $.append
[isAttached
] may not reflect the actual state of attachment.
A view is detached when its el
is removed from the DOM.
The best time to clean up any listeners added to the el
is in the before:detach
event.
While the el
of the view may remain attached, its contents will be removed on render.
If you have added listeners to the contents of the view rather than before:detach
the
dom:remove
event would be best.
Destroying a view (ie: myView.destroy()
) cleans up anything constucted within Marionette so that if
a view's instance is no longer referenced the view can be cleaned up by the browser's garbage collector.
The before:destroy
event is the best place to clean
up any added listeners not related to the view's DOM.
The state of the view after the destroy is not attached and not rendered although the el
is not emptied.
Children added to a View
's region or through a CollectionView
will be automatically destroyed if the
view is re-rendered, if the view is destroyed, or for CollectionView
if the collection
is reset.
Note Children are removed after the DOM detach of the parent to prevent multiple reflows or repaints.