Build interfaces

5.0.0-beta.1 · Published beta · b06750c5. Published on npm. Match your installed version.

View Lifecycle#

Both View and CollectionView are aware of their lifecycle state which indicates whether the View is rendered, attached, or destroyed.

Documentation Index#

Lifecycle State Methods#

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.

State vectors#

The three lifecycle methods are independent observations, not one linear state enum. View construction can therefore produce any of the four alive render/attachment vectors:

Initial el isRendered() isAttached() isDestroyed()
Empty and detached false false false
Empty and in the document false true false
Populated and detached true false false
Populated and in the document true true false

CollectionView starts unrendered regardless of its initial contents and has its own lifecycle transition table.

With lifecycle monitoring enabled, Marionette-managed operations preserve the following observable transitions:

Operation Result Repeated call
View#render() with a template function while alive Runs before:render and render; rendered becomes true; attachment is unchanged Renders again and runs the render lifecycle again
View#render() with template: false while alive Returns the View without running the render lifecycle or changing contents or state Repeated calls are the same no-op
CollectionView#render() while alive Runs before:render and render, rebuilds its children, and becomes rendered; attachment is unchanged Rebuilds the children and runs the render lifecycle again
view.renderAttributes() while alive Applies the current root attribute declarations without changing contents, children, lifecycle events, or state Reevaluates and applies the declarations again
region.show(view) Ensures the view is rendered; attached becomes true only when the Region is in the document Showing the current view is a no-op
region.detachView() Rendered is preserved; attached becomes false; destroyed stays false Returns undefined with no transition
Re-show a detached view Rendered stays true; attachment reflects the Region Does not render the view again
region.empty() or view.destroy() Rendered and attached become false; destroyed becomes true Repeated destroy is a no-op
view.render() after destruction Returns the same View with rendered and attached false and destroyed true Repeated calls are no-ops
view.renderAttributes() once destruction begins Returns the same View before resolving declarations or changing the root element or lifecycle state Repeated calls are no-ops
CollectionView#addChildView(view, ...) once destruction begins Returns the supplied child before inspecting or taking ownership of it; the caller remains responsible for that child Repeated calls are no-ops for the destroyed CollectionView
view.delegateEvents() or view.undelegateEvents() once destruction begins Returns the same View without changing View or Behavior DOM delegation Repeated calls are no-ops
view.bindUIElements() once destruction begins Returns the same View without resolving host UI, querying DOM, or binding View or Behavior UI Repeated calls are no-ops

Setting monitorViewEvents: false on a Region's owning view intentionally disables attachment events and automatic isAttached() updates for the shown view.

This table specifies the managed and terminal operations listed above. Do not infer behavior for other calls on a destroyed View; custom overrides also own their behavior unless they delegate to a guarded base method.

Instantiating a View#

Every Marionette View and CollectionView has a native DOM element in el. Pass an existing element with el: document.querySelector('.foo-selector'), or create one first with document.createElement(). Selector strings and jQuery collections are not valid View el values.

When el is omitted, Marionette creates the root element from tagName (a div by default) and applies the resolved id, className, and attributes. The element remains the View's root for its entire lifetime. Native core does not create $el; applications can initialize their own wrapper when using the jQuery adapter.

Marionette determines whether the initial root is already rendered or attached. If a View starts rendered or attached, its state reflects that status, but the related events will not have fired. An element owned by template content is detached while that owner document has no document element. Showing its View later through an attached Region runs the managed attachment lifecycle once for the View and its existing children.

For more information on instantiating a view with pre-rendered DOM, see Pre-rendered Content.

A fixed root element#

Choose the root with the constructor's el option, or let Marionette create it. A View and its Behaviors keep that element for their lifetime. el is readonly in the public instance types; assigning another element directly is unsupported. There is no public setElement() method.

Rendering changes the root's contents. Moving or detaching a View through a Region preserves its root and its child ownership. If another system replaces the root, destroy the old View and construct a new View with the new element. Keep state that must survive that replacement outside the View.

Rendering a 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 A completed render leaves the View rendered until destruction. During a normalized collection update, CollectionView may mark an updated child unrendered before rendering it again; a filtered child can remain unrendered until it becomes visible.

View Rendering#

For View, rendering with a template function runs the before:render lifecycle, serializes the View's data, passes it to the template, places the result in el, binds UI, marks the View rendered, and then runs the render lifecycle. A newly constructed View is already considered rendered if its initial el contains content. A later template may produce empty content; the completed render still leaves the View rendered.

template: false is different from a template that returns an empty value. Calling View#render() with template: false returns the View without running the render lifecycle, changing the DOM, or changing its rendered state.

CollectionView Rendering#

For CollectionView, every live render() is bracketed by before:render and render. After it completes, collection-backed children have been rebuilt, the optional template and visible children have been rendered, and the CollectionView is rendered. Any children the CollectionView owned before that render have been destroyed.

Inserting a child element into the CollectionView is not itself an attachment transition. When the CollectionView is monitored as attached, rendering marks and notifies the inserted children as attached; when the parent is detached or child lifecycle monitoring is disabled, their monitored attachment state remains detached even though their elements are inside the parent element.

A CollectionView with no children is still rendered, with or without an emptyView. Its own template controls the container markup but does not determine rendered state.

Rendering Children#

Rendering child views is often best accomplished after the View renders, as the first render typically happens before the View enters the DOM. This helps to prevent unnecessary repaints and reflows by making the DOM insertion at the highest practical View in the view tree.

The exception is Views with pre-rendered content. When a View is instantiated rendered, child Views are best managed in the View's initialize.

View Children#

In general the best method for adding a child view to a View is to use showChildView in the render event.

View Regions are emptied on each render, so Views shown outside of the render event still need to be shown again on subsequent renders.

CollectionView Children#

The primary use case for a CollectionView is maintaining collection-backed child Views. Marionette creates and removes those children as the collection changes.

addChildView() can also add a child that is independent of the collection, but that child is not unmanaged. The CollectionView owns it, includes it in its child containers, and may sort or filter it. Rendering, collection reset, or CollectionView destruction destroys every child that is still owned, including manually added children. detachChildView() is the explicit operation that removes a child from ownership without destroying it and transfers cleanup responsibility to the caller.

See Self-Managed children for the supported add, remove, detach, sorting, and filtering contracts.

Attaching a View#

isAttached() is Marionette's monitored lifecycle state, not a live query of the physical DOM on every call. Construction initializes it from the current root element, and Marionette-managed Region and CollectionView operations update it while attachment monitoring is enabled. The attach event is the appropriate place to add listeners to the root el. Render can replace the contents while that root remains attached; use dom:refresh for listeners tied to those rendered descendants.

Moving view.el directly with native DOM APIs, such as document.body.append(view.el), changes its physical location without running Marionette attachment lifecycles or updating isAttached(). The same caveat applies when application code directly removes or moves an attached root. Prefer a Region or CollectionView for managed transitions; if application code moves the element directly, it owns the resulting lifecycle mismatch.

A child shown in a rendered but detached parent View's Region is rendered and remains detached. When the parent is later shown in an attached Region, attachment propagates to its existing children. A child shown during the parent's onAttach is attached immediately. Showing the same attached parent again is a no-op for both parent and child attachment lifecycles.

Detaching a View#

A managed View becomes detached when Marionette removes its el from the DOM and updates its monitored attachment state. Use the before:detach event to clean up listeners added to the root el. Render can replace descendants while the root remains attached; use dom:remove to clean up listeners tied to those rendered descendants.

Detaching a parent View propagates detachment to its managed Region children while preserving their rendered state and ownership. Re-showing that parent attaches the same children again. Emptying the parent-owning Region then detaches and destroys the parent and its still-managed children once.

Destroying a View#

Destroying a View (for example, myView.destroy()) removes Marionette-owned resources: delegated View and Behavior DOM handlers, bound UI, outgoing listenTo() subscriptions, entity-event bookkeeping, Behaviors, Regions and their current Views, and CollectionView children that remain owned. It detaches the root element and leaves the View rendered false, attached false, and destroyed true after successful teardown.

Destroy does not remove callbacks registered directly on the View with on(), destroy its model, collection, or arbitrary option collaborators, or clean up application resources Marionette does not own. Release those resources in the appropriate lifecycle callback.

The before:destroy event is the best place to clean up any added listeners not related to the view's DOM.

Once destruction begins, reentrant destroy() calls from before:destroy or destroy, and later repeated calls, return the same View without restarting teardown. During a normal successful teardown, an attached parent and its owned children complete their detach and destroy lifecycles once.

Base View#bindUIElements() and CollectionView#bindUIElements() calls are also terminal no-ops once destruction begins. They do not resolve callable UI, query the retained root element, or bind attached Behaviors. A direct Behavior#bindUIElements() call through a Behavior owned by or retained from that host returns the Behavior without binding. unbindUIElements() remains available for cleanup, and getUI() continues to throw MN0023 when UI is unbound.

Errors from lifecycle handlers propagate and stop the operation, as described under Synchronous failures. A throwing before:destroy or later cleanup handler does not clear the destruction guard or make a later destroy() call resume teardown.

Successful destruction retains the root el object but detaches it. Do not infer that all of its contents are retained: owned child Views are removed as they are destroyed, and Region or CollectionView cleanup can detach contents from managed containers. Marionette makes no general cleanup promise for unowned DOM outside those managed boundaries.

Synchronous failures#

Marionette expects valid adapters and working registration and cleanup callbacks. An exception during synchronous registration, construction, rendering, or teardown propagates to the caller and aborts that operation. Completed work is not rolled back. Marionette does not promise to release every resource after a callback throws, restore a partially initialized or rendered instance, or recover on the next call or source notification. Fix the failing callback or adapter; do not rely on partial instance state after a failure.

Successful cleanup and the documented ownership and repeated-destruction rules still apply. A callback that destroys or mutates an owner during an in-progress render does not acquire additional recovery guarantees merely because it calls a public method; use the documented lifecycle boundaries for that workflow.

Application's asynchronous lifecycle has its own readiness, cancellation, rejection, and restart semantics. This synchronous failure boundary does not replace those contracts or change ordinary supersession into an error.

Destroying Children#

Children still owned by a View's Region or a CollectionView are automatically destroyed when their owner completes a re-render or is destroyed. A CollectionView also destroys its currently owned children when its collection is reset before building the replacement collection-backed children. A child returned by detachView() or detachChildView() is no longer owned and is not included in later owner cleanup.

During owner destruction, children are removed after the parent root is detached to avoid repeated reflows or repaints.