# Marionette 5.0.0-beta.1 documentation bundle Version: 5.0.0-beta.1 Channel: next Publication: beta (published on npm) Source revision: b06750c507494441f0b2298766b70087e45346a2 Local changes: false Content SHA-256: 3fe8788a771994d9effd124fee94d7444637a27ec974a5b520ccbf43b55bbec9 Read individual documents for focused tasks. Each section below contains the exact published reading Markdown, including its source identity. --- Document: docs/readme.md Canonical URL: https://marionettejs.com/docs/ Markdown URL: https://marionettejs.com/docs/index.md Reading SHA-256: 7f73d9a4ff2f5ec0af5843ea7b954c0afc6160d925850f54e98cc702e060a668 # Build your first piece of UI A View handles a piece of the interface. A Region puts it on the page and cleans it up when it is replaced. Start there; add the other pieces when you need them. ## Where do you want to start? - **[Build something](/docs/installation.md#quick-start)** — set up Marionette and show your first View. - **[Work with an agent](/docs/agents.md)** — give your agent the right contract and a concrete task. - **[Look up an API](/docs/public-api.md)** — find the class, method, or integration you need. ## A button that does something With a [matching v5 build](/docs/installation.md#install) installed, add a place for the View in your HTML: ```html ``` Then run this module in your application: ```javascript import { Region, View } from 'marionette'; const Counter = View.extend({ initialize() { this.count = 0; }, template: ({ count }) => ``, templateContext() { return { count: this.count }; }, events: { 'click button': 'increment' }, increment() { this.count += 1; this.el.querySelector('span').textContent = String(this.count); } }); export const region = new Region({ el: '#app' }); region.show(new Counter()); ``` Click the button: **Count: 0 → Count: 1 → Count: 2**. The View handles the click and updates the number in place. The button stays the same DOM element. When that part of the screen is finished, `region.empty()` destroys the View and removes its event handlers. The `#app` mount remains, ready for the next View. ## Give it a little more to do | You want to… | Next step | | --- | --- | | Show a list that changes | [Render children with CollectionView](/docs/collection-view.md) | | Open a detail screen | [Show and replace a View](/docs/region.md) | | Save a form without losing a draft | [Forms and accessibility](/docs/forms-and-accessibility.md) | | Connect an existing router or data source | [Choose integrations](/docs/choosing-integrations.md) | | Check that it works | [Test an application](/docs/testing.md) | You can keep Backbone models, an existing router, or a preferred template system. Choose each integration for the job it does; the button above needs none of them. For versions before v5, see the [backbone.marionette repository](https://github.com/marionettejs/backbone.marionette). [Canonical source](/docs/markdown/docs/readme.md) · [Source identity](/docs/manifest.json) --- Document: docs/installation.md Canonical URL: https://marionettejs.com/docs/installation/ Markdown URL: https://marionettejs.com/docs/installation.md Reading SHA-256: fdd88cef2835ae795727e379c4d6e928eb88292020a0fc57c0ffb5a7e8808b77 # Installing Marionette Install the core package, show a View, then add the integrations your application needs. Native DOM APIs, plain objects, and function templates work out of the box. This guide covers the published Marionette 5.0.0-beta.1 package and its matching companion packages. ## Documentation Index * [Install](#install) * [Peer dependencies](#peer-dependencies) * [Quick start](#quick-start) * [TypeScript](#typescript) * [Independent runtimes](#independent-runtimes) * [Observable data sources](#observable-data-sources) * [Distribution formats](#distribution-formats) * [Backbone is optional](#backbone-is-optional) * [jQuery DOM adapter is optional](#jquery-dom-adapter-is-optional) * [DOM content adapters are optional](#dom-content-adapters-are-optional) * [Current v5 documentation](/docs/index.md) ## Install The v5 package name is `marionette`. ```bash npm install marionette@5.0.0-beta.1 ``` The beta is available on npm. Pin this exact version and use the matching documentation. For unreleased changes, build and pack a known source revision separately. > The v4 package name has changed. See the [upgrade guide](/docs/upgrade-guide.md) > for migration guidance from earlier releases. Core and `@mnjs/data` automatically install the matching `@mnjs/utils` version. Applications do not need a separate install unless they import helpers directly. During prereleases, keep Marionette packages on the same version. See the [shared helpers](/docs/common.md#shared-helpers) for reusable component helpers. ## Peer dependencies Marionette v5 core has no peer dependencies. The separate `@mnjs/adapters` package requires the matching Marionette version and declares the integration-specific peers as optional. | Peer | Required? | When you need it | |---|---|---| | `marionette` `5.0.0-beta.1` | Required | The matching core runtime configured with an adapter. | | `backbone` `^1.4.0` | Optional | Only if your app imports `@mnjs/adapters/backbone`. See [Backbone is optional](#backbone-is-optional). | | `@types/backbone` `^1.4.23` | Optional | TypeScript declarations for `@mnjs/adapters/backbone`. JavaScript consumers do not need it. | | `jquery` `^4.0.0` | Optional | Only if your app uses the `@mnjs/adapters/dom/jquery` adapter. See [jQuery DOM adapter is optional](#jquery-dom-adapter-is-optional). | | `@types/jquery` `^4.0.1` | Optional | TypeScript declarations for `@mnjs/adapters/dom/jquery`. JavaScript consumers do not need it. | | `morphdom` `^2.7.8` | Optional | Only if your app imports `@mnjs/adapters/dom/morphdom`. | | `lit-html` `^3.3.3` | Optional | Only if your app imports `@mnjs/adapters/dom/lit-html`. | Optional peers are installed only when you opt into them: ```bash # Only if you use the Backbone integration npm install @mnjs/adapters@5.0.0-beta.1 backbone # Only if you use the jQuery DomApi adapter npm install @mnjs/adapters@5.0.0-beta.1 jquery # Only if you use XState actors npm install @mnjs/adapters@5.0.0-beta.1 xstate ``` The XState actor adapter does not import or declare XState as a peer. Install XState alongside the adapter; the adapter consumes its public actor shape. Npm does not install missing optional peers. TypeScript consumers of an optional subpath must install its matching type package explicitly: ```bash # Only if TypeScript imports @mnjs/adapters/backbone npm install --save-dev @types/backbone@^1.4.23 # Only if TypeScript imports @mnjs/adapters/dom/jquery npm install --save-dev @types/jquery@^4.0.1 ``` Marionette core does not import or require Underscore. Install it as an application dependency only when your own code uses it, such as an `_.template` used by a View. ## Quick start Marionette v5 exposes its public API through named ESM imports. There is no default-namespace export; use named imports only. Add a mount element to the page before running the module: ```html
``` ```js import { Application, View } from 'marionette'; const RootView = View.extend({ template: () => 'Content
' }); ``` These properties are defined by Marionette's standalone `View` constructor. When Marionette creates the View's element, it copies own enumerable `attributes` properties, including symbols. The default DomApi applies string attribute names only; inherited and non-enumerable properties are not copied. When applied, `id` and `className` assignments occur afterward and override the corresponding `attributes` keys. See the [`DomApi.setAttributes` contract](/docs/dom-api.md#setattributesel-attrs). ## Method results and side effects These operations run synchronously. Use lifecycle hooks for additional work; returning a Promise from a View hook does not delay rendering or destruction. | Method | Result | Effect | | --- | --- | --- | | `render()` | This View | Evaluates the template, updates contents and UI bindings. Rendering again resets its Regions and destroys their current children. `template: false` and a destroyed View make this a no-op. | | `renderAttributes()` | This View | Refreshes root attributes without rendering contents or recreating children. | | `destroy(options)` | This View | Removes the root element, destroys owned Regions/children and Behaviors, releases subscriptions and owned State. Repeated destruction is a no-op. | | `isRendered()`, `isAttached()`, `isDestroyed()` | Boolean | Read lifecycle state without rendering. Attachment is Marionette's tracked state; see [monitoring](/docs/lifecycle.md). | | `hasRegion(name)`, `getRegion(name)` | Boolean or Region/`undefined` | Read a named registration without rendering the parent. | | `getRegions()` | New name-to-Region object | Read registrations; changing this object does not change ownership. | | `showChildView(name, view, options)` | Supplied child View | Renders the parent if needed, then delegates to the named Region. The result alone does not establish adoption when `allowMissingEl` permits a missing mount. | | `getChildView(name)` | Current child or `undefined` | Renders the parent if needed before reading the named Region. | | `detachChildView(name)` | Detached child or `undefined` | Renders the parent if needed, then transfers a live child to the caller. | | `addRegion(name, definition)` | Registered Region | Constructs or registers a Region without rendering the parent. | | `addRegions(definitions)` | Map of added Regions, or `undefined` for no entries | Registers the batch; see [ownership constraints](/docs/region.md#reading-region-ownership). | | `removeRegion(name)` | Removed Region | Destroys that Region and its current child. | | `removeRegions()` | Map of removed Regions | Destroys every registered Region and its current child. | | `emptyRegions()` | Map of Regions | Renders the parent if needed, destroys current children, and keeps the Regions available. | `getChildView`, `showChildView`, `detachChildView`, and `removeRegion` require a registered name and throw [`MN0020`](/errors/MN0020.md) when it is absent. `getRegion` returns `undefined` for an absent valid name. Region names must be non-empty strings; an empty string throws [`MN0032`](/errors/MN0032.md). A supplied `state` is borrowed rather than copied as a normal constructor option. See [State ownership](/docs/state.md#borrowed-and-owned-sources) for `getState()`, `createState()`, subscriptions, and disposal. ## Rendering a View The Marionette View implements a powerful render method which, given a [`template`](/docs/rendering.md#setting-a-view-template), will build your HTML from that template, mixing in `model` or `collection` data and any extra [template context](/docs/rendering.md#adding-context-data). Marionette `View` defines `render`, and this method should not be overridden. To add functionality around rendering, use the [`render` and `before:render` events](/docs/class-events.md#render-and-beforerender-events). For more detail on how to render templates, see [View Template Rendering](/docs/rendering.md). ### Using a View Without a Template With [`template: false`](/docs/rendering.md#using-a-view-without-a-template), `render()` returns the View without changing its contents or running `before:render` and `render`. Other View events and DOM interactions remain available. Use this for [`prerendered content`](/docs/prerendered-dom.md) that the View should preserve. ### Refreshing Root Attributes `renderAttributes()` reevaluates a View's declarative `attributes`, `className`, and `id`, then applies those values to its existing root element. The method is also available on `CollectionView`. ```javascript import { View } from 'marionette'; const SelectableRow = View.extend({ tagName: 'tr', attributes() { return { 'aria-selected': this.isSelected ? 'true' : 'false' }; }, className() { return this.isSelected ? 'danger' : null; }, template: false, setSelected(isSelected) { this.isSelected = isSelected; return this.renderAttributes(); } }); const row = new SelectableRow(); const rootElement = row.el; row.setSelected(true); export { rootElement, row }; ``` With the default DomApi, only an explicit `null` removes an attribute. An `undefined` value or omitted key leaves the existing attribute untouched; Marionette does not retain the names returned by an earlier call. Other values, including `false`, `0`, and an empty string, use the browser's attribute string conversion. For boolean HTML attributes, declare `disabled: isDisabled ? '' : null`; `disabled: false` still creates a present attribute and disables the element. `id` and `className` continue to override matching keys from `attributes` when they are declared. Live form properties such as `input.value` and `input.checked` should be updated explicitly, separately from their default-value attributes. Use `className` as the View-level class declaration, as shown above. The `attributes` map continues to use raw DOM attribute names for lower-level cases. Marionette normalizes the View declaration to the `class` attribute before calling the DomApi, including for a supplied SVG root. `renderAttributes()` returns the View. It does not call the template, emit the render lifecycle, replace the root element, rebind `ui` or DOM events, or reset Regions. It is not called automatically by `render()`. Calls after destruction begins are no-ops and do not resolve the attribute declarations. When a View uses a supplied `el`, construction still leaves that element's attributes unchanged. A later `renderAttributes()` call applies only the keys in the current declaration, so unrelated host attributes remain caller-owned. ## View Lifecycle and Events An instantiated `View` is aware of its lifecycle state and will throw events related to when that state changes. The view states indicate whether the view is rendered, attached to the DOM, or destroyed. Read More: - [View Lifecycle](/docs/lifecycle.md) - [View DOM Change Events](/docs/class-events.md#dom-change-events) - [View Destroy Events](/docs/class-events.md#destroy-events) ## Entity Events A `View` subscribes to its `model` and `collection` through the configured [DataApi](/docs/data-api.md). Event names and callback arguments belong to that data provider. Plain objects and arrays do not emit changes; declaring entity event maps for unobservable values throws `MN0037`. Read More: - [Entity Events](/docs/entity-events.md) ## DOM Interactions `View` provides `events`, `triggers`, and `ui` for DOM interactions. Read More: - [DOM Interactions](/docs/dom-interactions.md) ## Behaviors A `Behavior` provides a clean separation of concerns to your view logic, allowing you to share common user-facing operations between your views. Read More: - [Using `Behavior`s](/docs/behavior.md#using-behaviors) ## Managing Children `View` provides a simple interface for managing child-views with [`showChildView`](#showing-a-child-view), [`getChildView`](#accessing-a-child-view), and [`detachChildView`](#detaching-a-child-view). These methods all access `regions` within the view. We will cover this here but for more advanced information, see the [documentation for regions](/docs/region.md). ### Laying Out Views - Regions The `View` class lets us manage a hierarchy of views using `regions`. Regions are a hook point that lets us show views inside views, manage the show/hide lifecycles, and act on events inside the children. **This Section only covers the basics. For more information on regions, see the [Regions Documentation.](/docs/region.md)** Regions are ideal for rendering application layouts by isolating concerns inside another view. This is especially useful for independently re-rendering chunks of your application without having to completely re-draw the entire screen every time some data is updated. Regions can be added to a View at class definition, with [`regions`](/docs/region.md#defining-regions), or at runtime using [`addRegion`](/docs/region.md#adding-regions). When you extend `View`, we use the `regions` attribute to point to the selector where the new view will be displayed: ```javascript import _ from 'underscore'; import { View } from 'marionette'; const MyView = View.extend({ template: _.template(` `), regions: { firstRegion: '#first-region', secondRegion: '#second-region' } }); ``` When we show views in the region, the contents of `#first-region` and `#second-region` will be replaced with the root element of the child View we show. The string values in this example are CSS selectors scoped to the `View`'s `el`. ### Showing a Child View To show a view inside a region, simply call `showChildView(regionName, view)`. This will handle rendering the view's HTML and attaching it to the DOM for you: ```javascript import { View } from 'marionette'; const ChildView = View.extend({ template() { return 'Content
'; } }); const ParentView = View.extend({ template() { return ` `; }, regions: { firstRegion: '.first-region', secondRegion: '.second-region' } }); export function runViewChildRegionLifecycle() { const parentView = new ParentView(); parentView.showChildView('firstRegion', new ChildView()); const childView = parentView.getChildView('firstRegion'); parentView.detachChildView('firstRegion'); parentView.showChildView('secondRegion', childView); parentView.getRegion('secondRegion').empty(); return parentView; } ``` Note: If `view.showChildView(region, subView)` is invoked before the `view` has been rendered, it will automatically render the `view` so the Region's `el` exists within the parent root; the root may still be detached. ### Accessing a Child View To access the child view of a `View` - use the `getChildView(regionName)` method. This will return the view instance that is currently being displayed at that region. The example gets the exact `ChildView` shown in `firstRegion` before moving it. If the named Region exists but has no current View, `getChildView` returns `undefined`. ### Detaching a Child View You can detach a child view from a Region through `detachChildView(regionName)`. It returns the same live, rendered View so that it can be shown again without rendering a second time. In the example, the parent detaches its child from `firstRegion` before showing that same child in `secondRegion`. This is a proxy for [Region `detachView()`](/docs/region.md#detaching-existing-views). ### Destroying a Child View To destroy and clear a child owned by a View, empty its owning Region. The example calls `parentView.getRegion('secondRegion').empty()`, which destroys the current child and leaves `secondRegion` empty and available for another View. ### Region Availability Defined regions are registered during `View` construction. `hasRegion(name)`, `getRegion(name)`, and `getRegions()` query the View's own Region registry without rendering, including when the View is unrendered or destroyed. `getRegions()` returns a fresh, safe own-key snapshot. Child View operations such as `showChildView`, `detachChildView`, and `getChildView` still render a live, unrendered View before dispatching through any `getRegion` override. `emptyRegions()` likewise renders before calling the overridable `getRegions()` and emptying its returned snapshot. Calling `getRegion(name)` does not render the parent or resolve the Region element. Calling the returned Region's `show(view)` resolves its element but does not render the parent. Use `showChildView`, or render the parent first, when showing a child into a declared selector Region. `getRegion(name)` and `hasRegion(name)` support optional lookup: an unknown name returns `undefined` or `false`, respectively. Operations that require a Region — `showChildView`, `detachChildView`, `getChildView`, and `removeRegion` — throw a `RegionError` with code [`MN0020`](/errors/MN0020.md) when the named Region does not exist. Region names must be non-empty strings. The public types require strings; an empty name throws a `RegionError` with code [`MN0032`](/errors/MN0032.md). Child View operations reject empty names before rendering the parent. ## Efficient Nested View Structures Show a parent's Region children in `onRender` when they should be recreated with that parent's template. During initial display, this builds the nested View tree before the owning Region attaches the parent. Keep independently editable content in child Views and update those children without re-rendering the parent when their state must survive. ```javascript import { View } from 'marionette'; const ParentView = View.extend({ // ... onRender() { this.showChildView('header', new HeaderView()); this.showChildView('footer', new FooterView()); } }); myRegion.show(new ParentView()); ``` Child Views can show their own Region children in `onRender` too. Marionette coordinates the render and attachment lifecycles; browser layout and paint counts depend on the DOM, styles, and application callbacks. Measure those costs in the running application when they matter. ## Listening to Events on Children Using regions lets you listen to the events that fire on child views - views attached inside a region. This lets a parent view take action depending on what events are triggered in views it directly owns. Read More: - [Child Event Bubbling](/docs/events.md#event-bubbling) [Canonical source](/docs/markdown/docs/marionette.view.md) · [Source identity](/docs/manifest.json) --- Document: docs/marionette.region.md Canonical URL: https://marionettejs.com/docs/region/ Markdown URL: https://marionettejs.com/docs/region.md Reading SHA-256: fc4d0668c1d52763debb29380efbdadb42c3f989d351314e3cb3fd4dfc97b3c7 # Marionette.Region A `Region` gives a changing part of the screen a place to live. Show a view, replace it with another, or empty the Region when that part of the interface is no longer needed. By default, replacing or emptying a view destroys it; the Region remains available for the next view. `Region` includes: - [Common Marionette Functionality](/docs/common.md) - [Class Events](/docs/class-events.md#region-events) - [The DOM API](/docs/dom-api.md) See the documentation for [laying out views](/docs/view.md#laying-out-views---regions) for an introduction in managing regions throughout your application. Regions maintain the [View's lifecycle](/docs/lifecycle.md) while showing or emptying a view. ## Documentation Index * [Instantiating a Region](#instantiating-a-region) * [Reading Region ownership](#reading-region-ownership) * [Lifecycle transition contract](#lifecycle-transition-contract) * [Defining the Application Region](#defining-the-application-region) * [Defining Regions](#defining-regions) * [String Selector](#string-selector) * [Additional Options](#additional-options) * [Specifying `regions` as a Function](#specifying-regions-as-a-function) * [Using a RegionClass](#using-a-regionclass) * [Referencing UI in `regions`](#referencing-ui-in-regions) * [Adding Regions](#adding-regions) * [Removing Regions](#removing-regions) * [Using Regions on a view](#using-regions-on-a-view) * [Showing a View](#showing-a-view) * [Checking whether a region is showing a view](#checking-whether-a-region-is-showing-a-view) * [Wrapping a non-Marionette view](#wrapping-a-non-marionette-view) * [Emptying a Region](#emptying-a-region) * [Preserving Existing Views](#preserving-existing-views) * [Detaching Existing Views](#detaching-existing-views) * [`reset` A Region](#reset-a-region) * [`destroy` A Region](#destroy-a-region) * [Check If View Is Being Swapped By Another](#check-if-view-is-being-swapped-by-another) * [Set How View's `el` Is Attached and Detached](#set-how-views-el-is-attached-and-detached) * [Configure How To Remove View](#configure-how-to-remove-view) ## Instantiating a Region A `Region` accepts `el`, `parentEl`, `allowMissingEl`, and `replaceElement`. `el` is a native element or a selector; selector resolution is deferred until an operation needs the element. `parentEl` limits selector lookup and may be an element, document, or function returning one. `allowMissingEl` and `replaceElement` may also be functions; a boolean supplied to `show(view, options)` overrides the corresponding Region setting for that call. ```javascript import { Region } from 'marionette'; const myRegion = new Region({ el: '#content' }); ``` While regions may be instantiated and useful on their own, their primary use case is through the [`Application`](#defining-the-application-region) and [`View`](#defining-regions) classes. ## Reading Region ownership A Region registered on a View exposes that existing relationship through pure, read-only queries. `getOwner()` returns the owning View and `getName()` returns the Region's name within that View. Neither query renders the View, resolves the Region element, or changes ownership. A standalone Region returns `undefined` from both methods. Removing a registered Region or completing its destruction clears both values. A throwing lifecycle hook interrupts teardown without rolling back ownership or retrying destruction. A Region has one authoritative registration. Re-adding that same Region instance under its current owner and name returns it without lifecycle events or ownership changes. Registering it under a different owner or name, registering a Region whose destruction has begun or completed, or replacing an occupied Region name through `addRegion` throws [`MN0030`](/errors/MN0030.md) before committing the conflicting registration. A conflict found before `addRegions` starts rejects the whole batch. Lifecycle hooks must not re-register the Region or occupy its registration name while registration is in progress. Failed batch registration is not rolled back. Remove an existing named Region before replacing it, and use a fresh Region instance when another View needs a Region. ```javascript const contentRegion = myView.getRegion('content'); contentRegion.getOwner() === myView; // true contentRegion.getName(); // 'content' ``` ## Lifecycle transition contract A Region owns at most one current View. Its public lifecycle state can be read without changing it: | State | `hasView()` | `isDestroyed()` | `currentView` | | --- | --- | --- | --- | | Empty | `false` | `false` | `undefined` | | Occupied | `true` | `false` | The View shown by the Region | | Destroyed | `false` | `true` | `undefined` | `isSwappingView()` is a temporary operation flag rather than a fourth stable state. It is `true` while one occupied Region replaces its current View with another, including the Region's `before:show`, `before:empty`, `empty`, and `show` callbacks. It returns to `false` when `show` completes. `isReplaced()` independently reports whether `replaceElement` has temporarily replaced the Region element; it does not change which lifecycle operations are valid. | Operation | Empty Region | Occupied Region | Destroyed Region | | --- | --- | --- | --- | | `show(view)` when the Region element resolves | Renders the View if needed, shows it, and enters occupied. | Showing the same View is a no-op. Showing a different View destroys the old View and swaps to the new one. | Returns the Region without inspecting or changing the caller-owned View or resolving the element. | | `detachView()` | Returns `undefined`; state is unchanged. | Detaches and returns the live View, then enters empty. | Returns `undefined` without changing state or DOM or emitting lifecycle events. | | `empty()` | Returns the Region and, when its element resolves, removes unmanaged contents from that element. | Destroys the current View, clears `currentView`, and enters empty. | Returns the Region without resolving the element or changing lifecycle state or DOM. | | `reset()` | Empties the Region and resets its element reference. | Destroys the current View, enters empty, and resets the element reference. | Returns the Region without resolving the element or changing lifecycle state, DOM, or element caches. | | Current View is destroyed externally | No effect. | Runs the Region's empty lifecycle once, clears `currentView`, and enters empty. | No effect. | | `destroy()` | Runs the destroy lifecycle and enters destroyed. | Emits `before:destroy`, destroys and empties the current View, enters destroyed, then emits `destroy`. | Returns the Region without repeating cleanup or lifecycle events. | Successful `show`, `empty`, and `destroy` calls return the Region when their operation completes. With `allowMissingEl: true`, `show` instead returns `undefined` and leaves the current View unchanged when its element does not resolve. A View returned by `detachView()` remains the caller's responsibility until the same or another Region shows it or it is destroyed. After destruction, `show()`, `empty()`, and `reset()` return the Region without changing it, and `detachView()` returns `undefined`. As soon as destruction begins, `show()`, `detachView()`, and recursive `destroy()` calls are no-ops. `empty()` and `reset()` remain available during cleanup. A View passed to `show()` during or after destruction remains caller-owned and unchanged. A destroyed Region cannot be reused. When its current View destroys itself, the Region clears that View's ownership and releases the owning parent View's subscriptions to it. Later events on the destroyed child are no longer forwarded to the parent. The following example preserves a View by detaching it before showing it again. Calling `empty()` afterward destroys the View and returns the Region to its empty state. ```javascript import { Region, View } from 'marionette'; export function runRegionLifecycle() { const region = new Region({ el: '#content' }); const contentView = new View({ template() { return 'Content
'; } }); region.show(contentView); const detachedView = region.detachView(); region.show(detachedView); region.empty(); return region; } ``` ## Defining the Application Region The Application defines a single region `el` using the `region` attribute. This can be accessed through `getRegion()` or have a view displayed directly with `showView()`. Below is a short example: ```javascript import { Application } from 'marionette'; import SomeView from './view'; const MyApp = Application.extend({ region: '#main-content', onStart() { const mainRegion = this.getRegion(); // Has all the properties of a `Region` mainRegion.show(new SomeView()); } }); ``` For more information, see the [Application docs](/docs/application.md#application-region). ## Defining Regions In Marionette you can define a region with a string selector or an object literal on your `Application` or `View`. This section will document the two types as applied to `View`, although they will work for `Application` as well - just replace `regions` with `region` in your definition. Region declaration maps, including maps passed to `addRegions`, use own enumerable string keys in standard JavaScript own-key order. Inherited, symbol, and non-enumerable properties are ignored, and a numeric `length` property is an ordinary Region name rather than an array-like signal. Arrays, sparse arrays, and other array-like values are not supported as Region declaration maps. Named View Region operations require a non-empty string name. `addRegion`, `removeRegion`, `hasRegion`, `getRegion`, `showChildView`, `detachChildView`, and `getChildView` throw [`MN0032`](/errors/MN0032.md) for an empty name. The public types require strings; unsupported shapes have no guaranteed diagnostic. Ordinary collision names such as `constructor`, `toString`, and `__proto__` remain valid when explicitly registered. ### String Selector You can use a CSS selector string to define regions. ```javascript import { View } from 'marionette'; const MyView = View.extend({ regions: { mainRegion: '#main' } }); ``` `Region#getEl(selector)` resolves the selector within `parentEl`, or within the document when no parent is defined, and returns the first matching native DOM element. A custom `getEl` override must preserve that native-element return contract; do not return a `NodeList` or jQuery collection. To customize selector lookup through the DOM adapter, implement `findEl(context, selector)` instead. The v4 `DomApi#getEl` method is not part of the v5 DOM API. Selector lookup is deferred until a DOM operation such as `show()` needs it. During construction, `initialize` observes the configured selector string in `this.el`; constructing a Region does not query the document or dispatch through a `getEl` override. ### Additional Options You can define regions with an object literal. Object literal definitions expect an `el` property - the selector string to hook the region into. With this format is possible to define whether showing the region overwrites the `el` or just overwrites the content (the default behavior). Region defaults and object-literal definitions contribute their own enumerable properties, including symbols, through object spread. Inherited and non-enumerable properties are ignored when Marionette builds the Region options. To replace the Region's placeholder with the child View's root element, use `replaceElement: true`: ```javascript import { View } from 'marionette'; const ReplacementView = View.extend({ className: 'new-class', template: () => 'Replacement content
' }); const Layout = View.extend({ template: () => '', regions: { main: { el: '.overwrite-me', replaceElement: true } } }); export const view = new Layout().render(); export const placeholder = view.el.querySelector('.overwrite-me'); export const replacement = new ReplacementView(); // Rendering the parent creates the placeholder. Showing the child replaces it. view.showChildView('main', replacement); view.$('.overwrite-me').length; // 0 view.$('.new-class').length; // 1 ``` `showChildView()` replaces `.overwrite-me` with the child's `el`; rendering the parent alone does not. The `className` option takes a class name, without the `.` used in CSS selectors. Emptying the Region destroys its current child and restores the original placeholder. The parent View's own root remains unchanged. This is useful when a container requires particular direct children, such as a `table` body containing rows. Choose a child `tagName` valid for that container. ```js import { View } from 'marionette'; const MyView = View.extend({ regions: { regionDefinition: { el: '.bar', replaceElement: true } } }); ``` **Errors** An operation that needs the element throws `MN0004` when no `el` is configured, or `MN0005` when a selector finds no element and `allowMissingEl` is false. Construction alone does not resolve the selector. ### Specifying `regions` as a Function On a `View` the `regions` attribute can also be a [function returning an object](/docs/basics.md#functions-returning-values): ```javascript import { View } from 'marionette'; const MyView = View.extend({ regions(){ return { firstRegion: '#first-region' }; } }); ``` ### Using a RegionClass If you've created a custom region class, you can use it to define your region. ```javascript import { Application, Region, View } from 'marionette'; const MyRegion = Region.extend({ onShow(){ // Scroll to the middle const viewHeight = this.currentView.el.getBoundingClientRect().height; const regionHeight = this.el.getBoundingClientRect().height; this.el.scrollTop = viewHeight / 2 - regionHeight / 2; } }); const MyApp = Application.extend({ regionClass: MyRegion, region: '#first-region' }) const MyView = View.extend({ regionClass: MyRegion, regions: { firstRegion: { el: '#first-region', regionClass: Region // Don't scroll this to the top }, secondRegion: '#second-region' } }); ``` ### Referencing UI in `regions` The UI attribute can be useful when setting region selectors - simply use the `@ui.` prefix: ```javascript import { View } from 'marionette'; const MyView = View.extend({ ui: { region: '#first-region' }, regions: { firstRegion: '@ui.region' } }); ``` ## Adding Regions To add regions to a view after it has been instantiated, simply use the `addRegion` method: ```javascript import MyView from './myview'; const myView = new MyView(); myView.addRegion('thirdRegion', '#third-region'); ``` Now we can access `thirdRegion` as we would the others. You can also add multiple regions using `addRegions`. ```javascript import MyView from './myview'; const myView = new MyView(); myView.addRegions({ main: { el: '.overwrite-me', replaceElement: true }, sidebar: '.sidebar' }); ``` ## Removing Regions You can remove all of the regions from a view by calling `removeRegions` or you can remove a region by name using `removeRegion`. When a region is removed the region will be destroyed. ```javascript import { View } from 'marionette'; const MyView = View.extend({ regions: { main: '.main', sidebar: '.sidebar', header: '.header' } }); const myView = new MyView(); // remove only the main region const mainRegion = myView.removeRegion('main'); mainRegion.isDestroyed(); // -> true // remove all regions myView.removeRegions(); ``` ## Using Regions on a view In addition to adding and removing regions there are a few methods to help utilize regions. `hasRegion` and `getRegion` are pure own-registry queries, and `getRegions` returns a pure snapshot; none renders. Child View operations and `emptyRegions` first render a live, unrendered View before resolving or mutating Regions. - `getRegion(name)` - Request an own registered Region without rendering. - `getRegions()` - Return a fresh own-key snapshot of registered Regions without rendering. - `hasRegion(name)` - Check if a View has an own registered Region without rendering. - `emptyRegions()` - Render when needed, then empty all Regions returned by `getRegions()`. ## Showing a View Once a region is defined, you can call its `show` method to display the view: ```javascript const myView = new MyView(); const childView = new MyChildView(); myView.render(); const mainRegion = myView.getRegion('main'); // render and display the child View mainRegion.show(childView, { fooOption: 'bar' }); ``` The parent View must already be rendered before calling a selector Region's `show` directly. Use `showChildView('main', childView)` to render the parent when needed before showing the child. This is equivalent to a view's `showChildView` which can be used as: ```javascript const myView = new MyView(); const childView = new MyChildView(); // render and display the view myView.showChildView('main', childView, { fooOption: 'bar' }); ``` Both forms require a Marionette View instance. Construct a `View` explicitly when displaying a template or static content; Regions do not allocate hidden Views from View classes, functions, strings, or option objects. The [wrapper pattern](#wrapping-a-non-marionette-view) provides explicit ownership for legacy integrations. ```javascript import { View } from 'marionette'; myView.showChildView('header', new View({ template: () => 'Welcome to the site' })); ``` The argument after the View instance in `Region#show(view, options)` and `View#showChildView(name, view, options)` is a separate show-options object passed to the [events fired during `show`](/docs/class-events.md#show-and-beforeshow-events). For more information on `showChildView` and `getChildView`, see the [Documentation for Views](/docs/view.md#managing-children) **Errors** - A destroyed View throws `MN0007`. Other input shapes are unsupported; core does not guarantee a Marionette diagnostic for an invalid value. - An error will be thrown if the view is already managed by a Region or CollectionView, including a filtered or deferred CollectionView child. Detach it from that owner first. ### Checking whether a region is showing a view If you wish to check whether a region has a view, you can use the `hasView` function. This will return a boolean value depending whether or not the region is showing a view. ```javascript const myView = new MyView(); myView.render(); const mainRegion = myView.getRegion('main'); mainRegion.hasView() // false mainRegion.show(new OtherView()); mainRegion.hasView() // true ``` If you show a view in a region with an existing view, Marionette will [remove the existing View](#emptying-a-region) before showing the new one. ### Wrapping a non-Marionette view Regions and CollectionViews manage Marionette Views. They do not synthesize render or destroy events for Backbone Views or fall back to a `remove()` method. Keep a legacy integration inside a Marionette owner: ```javascript import { View } from 'marionette'; import LegacyView from './legacy-view.js'; const LegacyWrapper = View.extend({ template: () => '', onRender() { this.legacy?.remove(); this.legacy = new LegacyView({ el: this.$('.legacy')[0] }); this.legacy.render(); }, onDestroy() { this.legacy?.remove(); } }); ``` Show `new LegacyWrapper()` in the Region. The wrapper owns the legacy instance and translates its actual rendering and cleanup API. No global prototype mixin or compatibility flags are needed. ## Emptying a Region You can remove a view from a region (effectively "unshowing" it) with `region.empty()` on a region: ```javascript const myView = new MyView(); myView.showChildView('main', new OtherView()); const mainRegion = myView.getRegion('main'); mainRegion.empty(); ``` This will destroy the view, clean up any event handlers and remove it from the DOM. When a region is emptied [empty events are triggered](/docs/class-events.md#empty-and-beforeempty-events). Calling `empty()` after Region destruction completes returns the Region without resolving its element, changing the DOM, or emitting empty lifecycle events. **NOTE** If the region does _not_ currently contain a View it will detach any HTML inside the region when emptying. If the region _does_ contain a View, any HTML that doesn't belong to the View will remain. ### Preserving Existing Views If you replace the current view with a new view by calling `show`, it will automatically destroy the previous view. You can prevent this behavior by [detaching the view](#detaching-existing-views) before showing another one. ### Detaching Existing Views If you want to detach an existing view from a region, use `detachView`. ```javascript const myView = new MyView(); const myOtherView = new MyView(); const childView = new MyChildView(); // render and display the view myView.showChildView('main', childView); // ... somewhere down the line myOtherView.showChildView('main', myView.getRegion('main').detachView()); ``` **Note** Detaching transfers responsibility for the live View to the caller. Show it again in the same emptied Region or another Region when needed, or call `destroy()` when finished with it. ## `reset` A Region Resetting a live Region destroys its current View and restores its original `el` reference. An original selector is queried again by the next operation that needs it; an original DOM element is reused without a selector query. ```javascript const myView = new MyView(); myView.showChildView('main', new OtherView()); const myRegion = myView.getRegion('main'); myRegion.reset(); ``` This can be useful in unit testing your views. Calling `reset()` after Region destruction completes returns the Region without changing its element reference or cache. ## `destroy` A Region A region can be destroyed which will `reset` the region, destroy its current View, remove it from any parent View's Region lookups, and stop any internal Region listeners. Reentrant Region destruction from `before:destroy` or `destroy`, repeated calls, and later destruction of the parent View do not repeat the child or Region teardown. A throwing lifecycle hook stops destruction. Later `destroy()` calls do not retry hooks or resume partial teardown. Discard the Region after a cleanup error; its remaining state is not a reusable lifecycle state. `isDestroyed()` becomes `true` after `reset()` finishes, before the `destroy` event. It remains `false` in `before:destroy`, `before:empty`, and `empty` handlers called during teardown. `destroy()` calls the overridable `reset()` method, which calls `empty()`. Overrides can use this ordinary synchronous chain while cleanup is in progress. An override that does not delegate to the base method owns the corresponding cleanup; for example, a custom `reset()` can call `this.empty()` and reset its own element reference. Nested `empty()` or `reset()` calls from lifecycle handlers are ordinary calls, so handlers must avoid recursive loops. After destruction completes, `empty()` and `reset()` return the Region without changing its element or DOM. `show()` and `detachView()` already stop accepting Views or transferring ownership as soon as destruction begins. ```javascript import { View } from 'marionette'; const MyView = View.extend({ regions: { mainRegion: '#main' } }); const myView = new MyView(); myView.render(); const myRegion = myView.getRegion('mainRegion'); myRegion.show(new ChildView()); myRegion.destroy(); myRegion.isDestroyed(); // true myRegion.hasView(); // false myView.hasRegion('mainRegion'); // false ``` ## Check If View Is Being Swapped By Another The `isSwappingView` method returns if a view is being swapped by another one. It's useful inside region lifecycle events / methods. The example will show an message when the region is empty: ```javascript import { Region } from 'marionette'; const EmptyMsgRegion = Region.extend({ onEmpty() { if (!this.isSwappingView()) { this.el.append('Empty Region'); } } }); ``` ## Set How View's `el` Is Attached and Detached Override the region's `attachHtml` method to change how the view is attached to the DOM (when not using `replaceElement: true`). This method receives one parameter - the view to show. The default implementation of `attachHtml` is essentially: ```javascript import { Region } from 'marionette'; Region.prototype.attachHtml = function(view){ this.el.appendChild(view.el); } ``` Similar to `attachHtml`, override `detachHtml` to determine how the region detaches the contents from its `el`. This method receives no parameters. For most cases you will want to use the [DOM API](/docs/dom-api.md) to determine how a region html is attached, but in some cases you may want to override a single Region class for situations like animation where you want to control both attaching and [view removal](#configure-how-to-remove-view). This example will make a view slide down from the top of the screen instead of just appearing in place: ```javascript import $ from 'jquery'; import { Region, View } from 'marionette'; const ModalRegion = Region.extend({ attachHtml(view){ // Some effect to show the view: const $el = $(this.el); $el.empty().append(view.el); $el.hide().slideDown('fast'); } }); const MyView = View.extend({ regions: { mainRegion: '#main-region', modalRegion: { regionClass: ModalRegion, el: '#modal-region' } } }); ``` ## Configure How To Remove View Override the region's `removeView` method to change how and when the view is destroyed / removed from the DOM. This method receives one parameter - the view to remove. The default implementation of `removeView` is: ```javascript import { Region } from 'marionette'; Region.prototype.removeView = function(view){ this.destroyView(view); } ``` `destroyView(view)` destroys a Marionette View and returns it. It forwards the Region owner's lifecycle-monitoring policy; it does not adapt a Backbone View or fall back to `remove()`. Keep this helper when overriding `removeView`. Region operations are synchronous. A `removeView` override must complete cleanup before returning if callers should observe the normal empty/destroy contract. Returning a Promise does not delay Region lifecycle completion. For an exit animation, finish the animation in the application before calling `empty()` or showing the replacement, and let the Region perform its normal synchronous teardown. The application owns cancellation when navigation or destruction interrupts that animation. [Canonical source](/docs/markdown/docs/marionette.region.md) · [Source identity](/docs/manifest.json) --- Document: docs/marionette.collectionview.md Canonical URL: https://marionettejs.com/docs/collection-view/ Markdown URL: https://marionettejs.com/docs/collection-view.md Reading SHA-256: cf11a246ff891a207873733884de682c188895c5d1ca138168760c61d4ea4cd1 # Marionette.CollectionView A `CollectionView` manages repeated parts of a screen: rows, cards, or any ordered set of child views within a root element, `el`. It creates children from a `collection`, or lets you add and remove child views yourself. Plain arrays work with the default [Data API](/docs/data-api.md). Use an adapter when your collection needs to notify the view about changes; mutating a plain array does not send those notifications. `CollectionView` includes: - [The DOM API](/docs/dom-api.md) - [Class Events](/docs/class-events.md#collectionview-events) - [DOM Interactions](/docs/dom-interactions.md) - [Child Event Bubbling](/docs/events.md#event-bubbling) - [Entity Events](/docs/entity-events.md) - [View Rendering](/docs/rendering.md) - [Prerendered Content](/docs/prerendered-dom.md) - [View Lifecycle](/docs/lifecycle.md) A `CollectionView` can have [`Behavior`s](/docs/behavior.md). ## Documentation Index * [Instantiating a CollectionView](#instantiating-a-collectionview) * [Rendering a CollectionView](#rendering-a-collectionview) * [Rendering a Template](#rendering-a-template) * [Defining the `childViewContainer`](#defining-the-childviewcontainer) * [Re-rendering the CollectionView](#re-rendering-the-collectionview) * [View Lifecycle and Events](#view-lifecycle-and-events) * [Entity Events](#entity-events) * [DOM Interactions](#dom-interactions) * [Behaviors](#behaviors) * [Managing Children](#managing-children) * [Attaching `children` within the `el`](#attaching-children-within-the-el) * [Destroying All `children`](#destroying-all-children) * [CollectionView's `childView`](#collectionviews-childview) * [Building the `children`](#building-the-children) * [Passing Data to the `childView`](#passing-data-to-the-childview) * [CollectionView's `emptyView`](#collectionviews-emptyview) * [CollectionView's `getEmptyRegion`](#collectionviews-getemptyregion) * [Passing Data to the `emptyView`](#passing-data-to-the-emptyview) * [Defining When an `emptyView` shows](#defining-when-an-emptyview-shows) * [Accessing a Child View](#accessing-a-child-view) * [CollectionView `children` Iterators And Collection Functions](#collectionview-children-iterators-and-collection-functions) * [Listening to Events on the `children`](#listening-to-events-on-the-children) * [Self Managed `children`](#self-managed-children) * [Adding a Child View](#adding-a-child-view) * [Removing a Child View](#removing-a-child-view) * [Detaching a Child View](#detaching-a-child-view) * [Swapping Child Views](#swapping-child-views) * [Sorting the `children`](#sorting-the-children) * [Defining the `viewComparator`](#defining-the-viewcomparator) * [Maintaining the `collection`'s sort](#maintaining-the-collections-sort) * [Filtering the `children`](#filtering-the-children) * [Defining the `viewFilter`](#defining-the-viewfilter) ## Instantiating a CollectionView When instantiating a `CollectionView` there are several properties, if passed, that will be attached directly to the instance: `attributes`, `behaviors`, `childView`, `childViewContainer`, `childViewEventPrefix`, `childViewEvents`, `childViewOptions`, `childViewTriggers`, `className`, `collection`, `collectionEvents`, `el`, `emptyView`, `emptyViewOptions`, `events`, `id`, `model`, `modelEvents`, `sortWithCollection`, `stateEvents`, `tagName`, `template`, `templateContext`, `triggers`, `ui`, `viewComparator`, `viewFilter` ```javascript import { CollectionView } from 'marionette'; const myCollectionView = new CollectionView(); ``` `CollectionView` composes the same visual, event, and State contracts as `View`, but does not inherit View's named-Region methods. Use `getEmptyRegion()` for its empty View; put a CollectionView inside a parent View when a layout needs additional named Regions. A supplied `state` follows the [State ownership contract](/docs/state.md#borrowed-and-owned-sources). ## Rendering a CollectionView The `render` method of the `CollectionView` is primarily responsible for rendering the entire collection. It loops through each of the children in the collection and renders them individually as a `childView`. ```javascript import { CollectionView } from 'marionette'; const MyCollectionView = CollectionView.extend({}); // all of the children views will now be rendered. new MyCollectionView().render(); ``` ### Rendering a Template In addition to rendering children, the `CollectionView` may have a `template`. The child views can be rendered within a DOM element of this template. The `CollectionView` will serialize either the `model` or `collection` along with context for the `template` to render. For more detail on how to render templates, see [View Template Rendering](/docs/rendering.md). ### Defining the `childViewContainer` By default the `CollectionView` will render the children into the `el` of the `CollectionView`. If you are rendering a template you will want to set the `childViewContainer` to be a selector for an element within the template for child view attachment. ```javascript import { CollectionView } from 'marionette'; const MyCollectionView = CollectionView.extend({ childViewContainer: '.js-widgets', template: () => 'Hello again.
' }); MessageView.setDomApi(MorphdomDomApi); ``` Morphdom updates the View's contents using its normal matching rules, including element IDs. Empty roots take the direct HTML insertion path. For Lit templates, select the Lit DOM adapter: ```javascript import { View } from 'marionette'; import { html } from 'lit-html'; import LitDomApi from '@mnjs/adapters/dom/lit-html'; const MessageView = View.extend({ template: ({ message }) => html`${message}
`, templateContext: { message: 'Hello again.' } }); MessageView.setDomApi(LitDomApi); ``` Both adapters apply template output through `Dom.setContents`. The root remains owned by the View; refresh its dynamic `className`, `id`, or `attributes` with [`renderAttributes()`](/docs/view.md#refreshing-root-attributes). A parent render still destroys its Region children. Keep Region placeholders empty so the renderer and Region do not manage the same contents. Lit replaces preexisting contents on its first explicit render. Keep `monitorViewEvents` enabled and manage attachment through Regions so directives receive connection changes through `Dom.notifyAttach(el)` and `Dom.notifyDetach(el)`. The View keeps the same root throughout its lifetime. Automatic directive connection management requires monitoring on the View and its ancestors. Lifecycle overrides must call their parent methods; avoid independently replacing Lit's contents or switching DOM adapters after rendering. See the [render adapter guide](/docs/adapters-package.md#dom-contents) for installation, directive cleanup, and root ownership. Rendering configuration is separate from data and state integration. Configure [`DataApi`](/docs/data-api.md) and [`StateApi`](/docs/state.md) explicitly when your sources need them. ## Serializing Data Marionette will automatically serialize the data from its `model` or `collection` through the configured [`DataApi`](/docs/data-api.md) for the template to use at [rendering](#rendering-the-template). You can override this logic and provide serialization of other data with the `serializeData` method. The method is called with no arguments, but has the context of the view and should return a javascript object for the template to consume. If `serializeData` does not return data the template may still receive [added context](#adding-context-data) or an empty object for rendering. ```javascript import _ from 'underscore'; import { View } from 'marionette'; const MyView = View.extend({ template: _.template(`