# 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/choosing-integrations.md Canonical URL: https://marionettejs.com/docs/choosing-integrations/ Markdown URL: https://marionettejs.com/docs/choosing-integrations.md Reading SHA-256: 8631640f38413a9035f386b1fb77c162b7c90b5777d8ebc7584b57dca004d684 # Choose integrations without changing the whole stack An adapter connects a specific capability to Marionette. It does not select the rest of your application architecture. Keep integrations that already satisfy the task; add a dependency only when the required behavior needs it. ## Make the decision in this order 1. **Inspect the project.** Read its package versions, initialization code, View subclasses, and existing adapter configuration. Follow its established integrations unless the requested change includes replacing them. 2. **Name the missing capability.** Examples: observe model changes, retain DOM contents across a render, or subscribe to an actor snapshot. “Use an adapter” is not itself a requirement. 3. **Use the smallest matching integration.** Keep Marionette's defaults for capabilities that do not need to change. Prefer an existing, verified adapter over introducing a custom implementation of the same contract. 4. **Check ownership and verification.** Identify who creates the source, who releases subscriptions, and which behavior demonstrates that the integration works. Configure it before constructing the affected owners. For a new application with no integration requirements, start with plain objects, arrays, native DOM operations, and template functions. Plain data is not observable: explicitly update the UI when it changes. If the task requires observable models and ordered collections without an existing provider, use [`@mnjs/data`](/docs/data-package.md) as the starting choice. Backbone models and collections are also observable: keep them and select `BackboneApi` when the application already uses Backbone. “Optional” means Backbone is not required by core, not that its data is static. `@mnjs/data` includes DataApi and StateApi implementations; it does not add persistence or REST synchronization. Choose another provider when a requirement calls for its additional behavior, such as state-machine actors or an existing persistence layer. ## Select each capability independently | Capability | Default | Change it when | Contract | | --- | --- | --- | --- | | Read models, serialize data, track collection identity/order, observe entity changes | Plain objects and array snapshots | Views consume another provider's models or collections | [DataApi](/docs/data-api.md) | | Subscribe to an owner's state and dispose owned state sources | Plain objects with no subscriptions | `stateEvents` must observe a provider, or owned sources need disposal | [StateApi](/docs/state.md#stateapi) | | Create, query, attach, and update DOM elements | Native browser APIs | Required DOM operations or content updates differ | [DomApi](/docs/dom-api.md) | | Evaluate a template with serialized data | Call a template function | A template engine needs another evaluation function | [Renderer](/docs/rendering.md#using-a-custom-renderer) | | Bind View/Behavior `events` and `triggers` declarations | Native delegated DOM events | The binding mechanism itself needs replacement | [EventDelegator](/docs/dom-interactions.md#eventdelegator-adapter) | | Match URLs and control browser history | None | The application requires routing | [Router integration](/docs/routing.md) | A state source and a View's model can use different providers. A single provider may implement both DataApi and StateApi, but configuring one does not configure the other. Changing DomApi does not change EventDelegator. A template renderer produces a value; DomApi applies that value to the element. ## Match an existing provider These entrypoints are supplied by this repository. Check their package and peer versions against the source revision or release you are using; an older alpha package may not contain an entrypoint described by current source docs. | Existing requirement | Integration | Scope and consequence | | --- | --- | --- | | Backbone models or collections | `@mnjs/adapters/backbone` as DataApi | Observes Backbone model and collection events while preserving their native vocabulary | | Backbone state | The same `BackboneApi` object as StateApi | A separate configuration decision from model/collection data | | XState actor data or state | `@mnjs/adapters/xstate` | Select the actor snapshot event explicitly; collection selectors return stable child actor references | | jQuery DOM queries or attachment operations | `@mnjs/adapters/dom/jquery` | Does not install jQuery event delegation or create `$el` | | Morphdom updates to a View's HTML contents | `@mnjs/adapters/dom/morphdom` | Keeps the View root; does not preserve child Views owned by Regions across parent render | | Lit template results | `@mnjs/adapters/dom/lit-html` | Applies Lit results through DomApi; requires attachment monitoring for directive connection cleanup | Read the [adapter package guide](/docs/adapters-package.md) for exact imports, provider constraints, ownership, and setup examples. There is no root `@mnjs/adapters` export. Import the subpath you use; importing it does not configure Marionette or select any other adapter. For example, a View may use Backbone data with native DOM operations and a plain template function. Adding Morphdom to its content updates would not require changing its models, state, or router. ## Configure the narrowest appropriate scope Configure a View subclass when the integration belongs to that component: ```javascript import { View } from 'marionette'; import BackboneApi from '@mnjs/adapters/backbone'; const AccountView = View.extend({ template: () => '', modelEvents: { change: 'render' }, onRender() { this.el.querySelector('.name').textContent = this.model.get('name'); } }); AccountView.setDataApi(BackboneApi); ``` This configures DataApi for `AccountView` and its subclasses. It does not choose StateApi or change sibling View classes. Use top-level setters when the whole application intentionally shares that configuration. Use an [isolated runtime](/docs/runtime-isolation.md) when independently configured application surfaces must coexist. Setters overlay supplied adapter methods. When composing DOM operations, configure a general adapter such as jQuery before an adapter that replaces content updates, such as Morphdom. Do not switch content adapters after a View has rendered. Changing a live object's source contract is an application migration, not a configuration shortcut. ## Add a custom adapter only for an unmet contract Before implementing one, write down: - The required methods and source event payloads, using the relevant contract. - Stable model identity and ordered collection snapshots, if it is a DataApi. - Borrowed versus owned sources, idempotent subscription cleanup, and which owner disposes each registration. - Failure behavior when subscription setup, rendering, or source updates throw. - A test with two consumers of one source, followed by destruction of one consumer. The surviving consumer must keep working. For editable collection children, also verify draft/focus retention when a model stays the same, and the documented destruction/recreation behavior when an immutable replacement supplies a different model with the same key. An adapter's type declaration alone does not establish these runtime behaviors. ## Explain choices to an agent or reviewer Record the chosen integration once where the application configures it. A useful decision states the capability, existing constraint, configuration scope, and verification, for example: > This feature already uses Backbone models. Configure BackboneApi on its View > subclasses, retain native DOM events, and verify that model changes render > once and that destroying one View leaves another observer subscribed. When several providers meet the same requirements, preserve the existing one. For a new project, choose the simplest option that meets the stated capability; ask for a preference only when the choice changes a meaningful product or maintenance constraint. Do not introduce additional providers just because examples for them appear beside each other in this guide. [Canonical source](/docs/markdown/docs/choosing-integrations.md) · [Source identity](/docs/manifest.json) --- Document: docs/marionette.state.md Canonical URL: https://marionettejs.com/docs/state/ Markdown URL: https://marionettejs.com/docs/state.md Reading SHA-256: 4fc7669d0f83247424ba50fefdc741db151703aec66c96d92284d1a51ea3471d # State sources and StateApi Keep state with the part of the application that uses it. `Application`, `MnObject`, `View`, `CollectionView`, and `Behavior` can each hold one state source. Marionette manages subscriptions and the cleanup described below; the source provides its own values and mutation API. `Region` does not compose state. `getState()` always returns the exact source. Core never converts a plain object into a model, record, Proxy, or observable object. ```javascript import { Application } from 'marionette'; const App = Application.extend({ createState() { return { filter: '', selectedId: null }; } }); const app = new App(); app.getState().filter = 'active'; ``` Without a supplied source or custom factory, the first `getState()` call lazily creates an empty plain object. An owner that never supplies, declares, or asks for state has no state-source property, subscription, or cleanup registration. ## Borrowed and owned sources Choose how the state source is created and who disposes it: - `state` is an already-created, borrowed source. Several owners may borrow the same source. Destroying one owner releases only its subscriptions and never disposes the source. - `createState(options)` is a factory called with the owner as `this` and the constructor options as its argument. Its result is owned. Owner destruction releases subscriptions and then calls the selected StateApi's optional `disposeOwned(source)` hook. A supplied function is a source, not a factory. Use `createState()` when a function must be invoked to create a source. State persists across View and CollectionView render. Application state persists across stop and restart. Behavior state lasts until that Behavior is destroyed, and MnObject state lasts until the object is destroyed. ## Plain-object state Plain objects are the dependency-free default and are intentionally non-observable. Mutate them with ordinary JavaScript and explicitly render or call an application method when the UI must update. ```javascript import { View } from 'marionette'; export const label = new View({ el: document.querySelector('#label'), model: { name: 'Account' }, template: model => model.name }).render(); const Disclosure = View.extend({ el() { return document.querySelector('#disclosure'); }, template: () => '', events: { 'click .toggle': 'toggle' }, createState() { return { open: false }; }, toggle() { const state = this.getState(); state.open = !state.open; this.render(); }, onRender() { this.el.dataset.open = String(this.getState().open); } }); export const disclosure = new Disclosure().render(); ``` ## StateApi The public adapter contract is deliberately small: ```javascript StateApi.subscribe(source, eventName, callback, context); // returns a cleanup function StateApi.disposeOwned?.(source); ``` `subscribe` registers handlers for future events. It receives each `stateEvents` name unchanged and calls the provided callback with the source's native payload. Every call must return an idempotent cleanup function. Marionette retains it outside the owner's public event registry and invokes it during destruction. Therefore calling `owner.off()` cannot disable state-source cleanup. Subscription setup errors propagate to the caller; event-map registration is not rolled back. `disposeOwned` is called only for a `createState()` result, after subscriptions are released. It is never called for a supplied or declared `state` source. The default StateApi does not pretend a plain object is observable. Declaring `stateEvents` for a source it cannot observe throws `MN0037`. Configure StateApi on the default runtime before constructing its consumers: ```javascript import { setStateApi } from 'marionette'; setStateApi({ subscribe(source, eventName, callback, context) { return source.subscribe(eventName, (...args) => callback.apply(context, args)); }, disposeOwned(source) { source.dispose(); } }); ``` `Application.setStateApi()`, `MnObject.setStateApi()`, `View.setStateApi()`, `CollectionView.setStateApi()`, and `Behavior.setStateApi()` configure a class or subclass independently. Repeated configuration overlays only that receiving class; it does not mutate its parent or sibling classes. StateApi selection is independent of DataApi selection, though one object may implement both. ## stateEvents `stateEvents` retains Marionette's declarative event-map shape. Handler names are resolved on the owner, while event vocabulary and callback arguments belong to the selected adapter. ```javascript import { View } from 'marionette'; // Fragment: provide an actor source and its matching StateApi at construction. const ActorView = View.extend({ stateEvents: { 'actor.transition': 'onTransition' }, onTransition(snapshot) { this.el.dataset.phase = snapshot.value; } }); ``` Changing from one state provider to another may require changing event names. Marionette does not add universal `get`, `set`, `reset`, `dispatch`, or `send` methods to state owners. ## Application lifetime ```javascript import { Application } from 'marionette'; const Session = Application.extend({ createState() { return { phase: 'stopped' }; }, onStart() { this.getState().phase = 'ready'; }, onStop() { this.getState().phase = 'stopped'; } }); export const session = new Session(); export const sessionState = session.getState(); export const started = await session.start(); export const stopped = await session.stop(); export const restarted = await session.restart(); ``` Application readiness remains the only asynchronous lifecycle boundary. Code that mutates a state source after awaited work must still check the readiness `AbortSignal` before committing stale results. ## Behavior lifetime ```javascript import { Behavior, View } from 'marionette'; const Disclosure = Behavior.extend({ events: { 'click .disclosure': 'toggleDisclosure' }, createState() { return { open: false }; }, toggleDisclosure() { const state = this.getState(); state.open = !state.open; this.view.render(); }, onRender() { this.view.el.dataset.disclosureOpen = String(this.getState().open); } }); const Settings = View.extend({ el() { return document.querySelector('#settings'); }, behaviors: [Disclosure], events: { 'click .selection': 'toggleSelection' }, template: () => '', createState() { return { selected: false }; }, toggleSelection() { const state = this.getState(); state.selected = !state.selected; this.render(); }, onRender() { this.el.dataset.selected = String(this.getState().selected); } }); export const settings = new Settings().render(); ``` A Behavior that receives its View's source through `state` borrows it. A Behavior-private `createState()` result is owned only by that Behavior. ## Migration from the v5 alpha State The experimental concrete `Marionette.State` export was removed from core. For non-observable local values, return a plain object from `createState()` and use property access. For reactive values, supply the provider's real source and a matching StateApi. Do not alias the removed State to another model type. ```javascript // Before const state = owner.getState(); state.set('open', true); ``` ```javascript // Plain-object source const state = owner.getState(); state.open = true; ``` [Canonical source](/docs/markdown/docs/marionette.state.md) · [Source identity](/docs/manifest.json) --- Document: docs/data.api.md Canonical URL: https://marionettejs.com/docs/data-api/ Markdown URL: https://marionettejs.com/docs/data-api.md Reading SHA-256: 1c38638bd95125841e3567665898932994feba558e097e09d0e5a567fd034378 # Data API Display plain objects and arrays directly, or connect your data library through `DataApi`. The adapter tells Marionette how to read models, obtain collection order, and observe changes. Core does not require Backbone-shaped `cid`, `attributes`, `get`, `models`, or collection event payloads. The default adapter treats models as plain objects and collections as ordered arrays. Plain arrays are static snapshots: mutating one does not notify Marionette. Call `render()` after changing a plain array. Declaring `modelEvents` or `collectionEvents` for an unobservable plain value throws `MN0037` instead of manufacturing an event system. Both Backbone models and collections (through `BackboneApi`) and `@mnjs/data` models and collections are observable alternatives; preserve an existing provider that meets the task. ```javascript import { CollectionView, View } from 'marionette'; const ChildView = View.extend({ tagName: 'li', template: model => model.name }); const ListView = CollectionView.extend({ childView: ChildView }); const models = [{ name: 'one' }, { name: 'two' }]; const list = new ListView({ collection: models }); list.render(); ``` ## Adapter contract An adapter supplies seven methods: | Method | Purpose | | --- | --- | | `key(model)` | Return a stable `Map` key used to associate a model with its child View. | | `get(model, attribute)` | Read one named value for string comparators and filters. | | `has(model, attribute)` | Distinguish a missing value from a present value of `undefined`. | | `serialize(model)` | Return the data passed to a template. | | `models(collection)` | Return the collection's current ordered model snapshot. | | `subscribe(entity, eventName, callback, context)` | Subscribe to an application entity event and return an idempotent cleanup function. | | `observeCollection(collection, callback, context)` | Observe structural collection changes and return an idempotent cleanup function. | `key()` must remain stable while a model belongs to a CollectionView and must be unique among the models currently owned by that CollectionView. The default adapter uses object identity. Adapters for immutable sources may use a stable source identity instead. `models()` must return an ordered model snapshot after the source mutation is complete. Marionette does not mutate that array. `subscribe()` registers handlers for future events and preserves the source event's arguments. It must return an idempotent cleanup function. Marionette invokes that function during explicit undelegation or owner destruction. Subscription setup errors propagate to the caller; event-map registration is not rolled back. `observeCollection()` also returns an idempotent cleanup function. Adapters are responsible for fulfilling these contracts; core does not wrap or validate each returned cleanup. `model` and `collection` are opaque adapter references. Only `null` and `undefined` mean no source; values such as `0`, `false`, and `''` can identify a source when the configured adapter supports them. Prefer a stable reference whose `get` and `serialize` methods read current values. Item changes can then notify existing Views through `subscribe` without replacing their identity. ## Collection observations `observeCollection()` reports one of three normalized records: ```javascript { kind: 'reorder' } { kind: 'reset' } { kind: 'update', added: [], removed: [], updated: [ { previous: previousModel, current: currentModel } ] } ``` `reorder` means model order changed without membership changing. `reset` means Marionette must rebuild every child. `update` supplies exact added and removed model instances. Each `updated` entry contains the previous and current model for one stable key. For an in-place update, `previous === current`. For an immutable same-key replacement, they are different objects. This distinction lets core distinguish a safe in-place render from an identity replacement. Marionette destroys and recreates the child View for an immutable same-key replacement so constructor options, `initialize`, Behaviors, entity events, and other model-dependent state all belong to the current object. Marionette constructs every same-key replacement View before removing any existing child. A replacement-construction or rendering failure propagates to the caller. Core does not undo a partial update or promise recovery on the next notification. See [synchronous failures](/docs/lifecycle.md#synchronous-failures). An in-place `updated` entry requests a child render. Adapters for mutable models with their own change events can leave `updated` empty and let child `modelEvents` handle rendering. The Backbone adapter follows this approach: merges still update collection order and filtering, without rendering children again after their model events have run. If a child was removed, detached, or destroyed while its model remained in the source, updates for that model do not recreate its View. Other children continue to update. Rendering the CollectionView again or a source reset recreates children from the current source. An immutable same-key replacement belongs only in `updated`, not in `removed` and `added`. Replacing a model with one that has a different stable key is a removal plus an addition; changing the key of a retained model is invalid. The post-mutation `models()` snapshot is authoritative and must agree with the record. Missing, duplicate, or unstable snapshot keys throw `MN0039`. Adapters must supply correct change records; core uses those records directly instead of recalculating the change to validate them. Added children follow the current snapshot order; removed children follow the previous snapshot order, regardless of their order in the change record. Observers may notify synchronously from CollectionView lifecycle hooks. Core captures each source snapshot and drains nested notifications in order, so each queued update uses the source state that accompanied it. All three record types enter one CollectionView reconciliation path. Additions create only their child Views; removals destroy only theirs; reorder moves survivor elements without rerendering them; and reset is the explicitly destructive whole-list operation. Presentation comparators may sort the child Views independently of the source's canonical order. ## Configuring an adapter Configure the application before constructing Views. In this configuration fragment, `MyDataApi` is the adapter your application supplies: ```javascript import { setDataApi } from 'marionette'; setDataApi(MyDataApi); ``` `setDataApi()` overlays the supplied own enumerable methods onto both `View` and `CollectionView`. `View.setDataApi()` and `CollectionView.setDataApi()` can configure a subclass independently. A CollectionView and its child View class must use compatible adapters. Behaviors use their owning View's adapter. Views and Behaviors work with the original model or collection, and event callbacks receive the source's native arguments. DataApi does not wrap application sources. Templates receive the data prepared by `serializeModel()` or `serializeCollection()`; see [Rendering](/docs/rendering.md). DataApi and [StateApi](/docs/state.md#stateapi) are selected independently. One adapter object may implement both contracts, but configuring one role never selects the other. ## XState actors `@mnjs/adapters/xstate` supports a parent XState v5 actor whose selected ordered collection contains stable child actor references. The adapter uses the actor reference itself as `DataApi.key()`, reads and serializes the child actor's current `snapshot.context`, and observes the parent through its snapshot subscription. A stopped and respawned actor is therefore a new model identity, even if it uses the same actor `id`. The following configuration fragment assumes `parentActor` is an already-created actor whose `context.children` contains stable child actor references. The application owns actor creation, startup, and eventual shutdown. ```javascript import createXStateActorApi from '@mnjs/adapters/xstate'; import { CollectionView, View } from 'marionette'; const XStateActorApi = createXStateActorApi({ select: snapshot => snapshot.context.children, snapshotEvent: 'actor:snapshot' }); const ChildView = View.extend({ template: context => context.label, modelEvents: { 'actor:snapshot': 'render', announced: 'onAnnounced' }, onAnnounced(event) { console.log(event.label); } }); const ListView = CollectionView.extend({ childView: ChildView }); ChildView.setDataApi(XStateActorApi); ListView.setDataApi(XStateActorApi); const view = new ListView({ collection: parentActor }).render(); ``` `snapshotEvent` is optional and has no implicit default. When configured, that exact event-map name observes `actor.subscribe()` snapshots. Every other name is passed unchanged to `actor.on()` and observes an explicitly emitted event; events sent to the actor are not surfaced automatically. The selected snapshot array should retain its reference for unrelated parent transitions. A newly subscribed observer does not receive an already-started actor's current snapshot, so initial template data comes from `getSnapshot()`. `select` is required when the result configures a CollectionView. Omit it when only actor model reads, `modelEvents`, or `stateEvents` are needed; that result does not define the collection-only `models()` and `observeCollection()` methods. Set the same adapter on `StateApi` when `stateEvents` should use this event vocabulary. Supplied actors are borrowed and never stopped by Marionette. An actor returned from `createState()` is owned and is stopped only after its Marionette-managed subscriptions are released. The adapter never traverses or stops child actors. ## Optional `@mnjs/data` sources Install `@mnjs/data` with `marionette` when an application wants a small first-party observable Model and ordered Collection without Backbone: ```sh npm install marionette@5.0.0-beta.1 @mnjs/data@5.0.0-beta.1 ``` ```javascript import { CollectionView, setDataApi, setStateApi, View } from 'marionette'; import { Collection, DataApi, Model, StateApi } from '@mnjs/data'; setDataApi(DataApi); setStateApi(StateApi); const RowView = View.extend({ tagName: 'li', template: () => '', modelEvents: { change: 'render' }, onRender() { this.el.textContent = this.model.get('label'); } }); const state = new Model({ selectedId: null }); const collection = new Collection([{ id: 1, label: 'one' }]); const list = new CollectionView({ tagName: 'ul', childView: RowView, collection, state }).render(); // Mount list.el in the application's chosen container. collection.get(1).set('label', 'updated'); // The existing row now shows "updated". ``` Unless `{ silent: true }` is passed, the package Collection emits synchronous `update`, `sort`, and `reset` events. The adapter translates them directly to normalized records. There is no separate observer queue, coalescing, or replay. Finish one structural mutation before starting another; schedule mutations from collection listeners or child lifecycle handlers after the current notification returns. Listener errors propagate and abort delivery. `move(modelOrId, index)` supports explicit list ordering without remove/add notifications or child View recreation. It and `sort` emit `sort`. Ordinary attribute changes use `model.set()` and child `modelEvents` bindings. The native adapter keys models by stable `cid`, so changing an application id does not replace its child View. Collection lookup uses current ids. Reset rejects duplicate instances and ids before changing membership; applications should keep ids unique when changing them. Lookup precedence is exact member instance, application id, then cid, regardless of collection order. Supplied native Model instances retain their identity even when the Collection configures a different model constructor; only raw attributes use that constructor. Bulk removal resolves all identities against one current snapshot, including ids changed with `{ silent: true }`. `Model.destroy()` and `Collection.destroy()` always emit their `destroy` lifecycle events, including with `{ silent: true }`. A destroyed model removes itself from each containing Collection through ordinary event subscriptions. Destroying a Collection releases its subscriptions without destroying its models. Use `Model.toObject()` for a shallow attribute copy and `Collection.toArray()` for an array of plain attribute objects. Template serialization reads attributes independently. The native package does not implement `toJSON`; pass these plain values to `JSON.stringify` explicitly. Define Model subclass `defaults` on the prototype with `Model.extend`, a prototype method, or a prototype getter; a native class field initializes too late to seed the base constructor. The package does not provide persistence, REST synchronization, validation, or implicit Backbone behavior. Native Model writes use `Object.is` equality and report sparse `changed` and `previous` maps on their event options. Nested writes are independent synchronous changes; use `options.changed` for the event being handled, since `model.changed` may already describe a nested write. `has` tests own-property presence, including undefined values. Native collection sorting is explicit and `reset` rebuilds children; there is no automatic merge/reconcile operation. See the package's [mutation semantics](/docs/data-package.md#mutation-semantics) for details. Applications using Backbone should import the bundled integration instead of configuring these methods individually. See [Optional Backbone](/docs/backbone.md). [Canonical source](/docs/markdown/docs/data.api.md) · [Source identity](/docs/manifest.json) --- Document: docs/dom.api.md Canonical URL: https://marionettejs.com/docs/dom-api/ Markdown URL: https://marionettejs.com/docs/dom-api.md Reading SHA-256: dea982ba190a485d792abec23e715bacab1a0bdd7dc1789dbff14785557c9709 # The DOM API Marionette uses a small DOM adapter for element creation, selection, attributes, content, and attachment operations. The default `DomApi` uses native browser APIs and does not require Backbone or jQuery. `View`, `CollectionView`, and `Region` expose their adapter as `Dom`. A custom adapter can replace only the operations an application needs; all omitted methods continue to use the inherited adapter. A renderer evaluates templates; `Dom.setContents` applies their output. The optional [Morphdom and Lit HTML DOM adapters](/docs/rendering.md#rendering-to-dom) preserve the selected DomApi; installing one does not select a data or state adapter. ## Element and selector boundaries `View` and `CollectionView` own a concrete DOM element. Their `el` option must be a DOM element. Resolve a selector at the call site when a View should reuse existing markup: ```javascript import { View } from 'marionette'; const view = new View({ el: document.querySelector('#content') }); ``` `Region` retains selector resolution because a Region locates its managed element relative to its `parentEl` or the document. `View#$()` and Region selector lookup both delegate to `DomApi.findEl`. With the native adapter, `View#$()` returns a `NodeList`. `Region#getEl` selects the first result and returns that native DOM element. This Region return contract does not change when `findEl` is supplied by the optional jQuery adapter. The v4 `DomApi#getEl` method is removed. DOM adapter overrides should implement `findEl(context, selector)` with an array-like result. Region `getEl` overrides are a separate extension point and must return one native DOM element. ## Native API methods The exported `DomApi` contains the following methods. This list is checked against the shipped package in CI. ### `createElement(tagName)` Creates and returns a DOM element with `document.createElement(tagName)`. Marionette uses it when a View does not receive an `el`. ### `createBuffer()` Creates and returns a `DocumentFragment` for collecting DOM nodes before one append operation. ### `getDocumentEl(el)` Returns `el.ownerDocument.documentElement`. Marionette uses that document root when determining whether a View is attached. Elements inside template content may have an owner document without a document element; Marionette treats that missing root as detached. ### `findEl(el, selector)` Finds descendants of `el` matching `selector`. The native adapter returns the `NodeList` produced by `el.querySelectorAll(selector)`. ### `hasEl(el, childEl)` Reports whether `childEl` is attached beneath `el`. Marionette uses this for attachment-state checks. ### `detachEl(el)` Removes `el` from its parent when it has one. Native listeners attached to the element remain on the detached element. ### `replaceEl(newEl, oldEl)` Replaces `oldEl` with `newEl` when `oldEl` has a parent. Passing the same element twice or an unattached `oldEl` is a no-op. ### `moveEl(el, parent, before)` Moves `el` within `parent` before the optional reference node. The native adapter uses `moveBefore` for already-attached children when available so CollectionView reordering and swapping preserve focus, selection, media, and custom-element connection state. It falls back to `insertBefore` for initial attachment and older DOM implementations; the CollectionView render pass restores focused text selection after that fallback, while older platforms may still run custom-element connection callbacks for the move. `swapChildViews()` does not restore focus or selection when it uses the `insertBefore` fallback without a child-render pass. ### `setContents(el, html)` Replaces the contents of `el` by assigning `html` to `el.innerHTML`. `null` and `undefined` produce empty contents. ### `setAttributes(el, attrs)` Applies own enumerable string keys from `attrs` as DOM attributes using `setAttribute`. Use attribute names such as `class` and `for`. View-level `className` is converted to `class` before this method is called. An explicit `null` removes an attribute. An `undefined` value or omitted key leaves the existing attribute untouched. Other values use the browser's string conversion, including `false`, `0`, and an empty string. For boolean HTML attributes, use `disabled: isDisabled ? '' : null`: the string `"false"` still means the attribute is present. ARIA and data attributes can use `false` to set `"false"`. This method does not assign JavaScript properties. Set live form values or custom element properties explicitly on the element; `value` and `checked` attributes describe input defaults. Attribute changes still have the browser's normal effects on reflected properties. When `View` or `CollectionView` creates an element, `id` and `className` declarations override matching entries in `attributes`. [`View#renderAttributes()`](/docs/view.md#refreshing-root-attributes) applies the current declarations to an existing element without tracking prior keys. Custom DomApi adapters must preserve explicit-null removal and leave undefined and omitted entries untouched. ### `appendContents(el, contents)` Appends the DOM node or `DocumentFragment` in `contents` to `el`. ### `hasContents(el)` Returns whether `el` exists and has child nodes. ### `detachContents(el)` Removes all children by assigning an empty string to `el.textContent`. This is the fast, jQuery-free default. ### `notifyAttach(el)` Notify the adapter that its element's contents are active. Called through View attachment monitoring and when construction adopts an attached root. The native implementation does nothing; Lit reconnects its directives. ### `notifyDetach(el)` Notify the adapter that its element's contents are inactive. Called through View detachment monitoring. This notification does not remove or empty the element. The native implementation does nothing; Lit disconnects its directives while retaining its rendered contents. These hooks receive only the element. They follow the existing attachment monitoring opt-out: with `monitorViewEvents: false` or monitoring handlers removed, applications must deliver the notifications they need themselves. This includes destruction: `destroy()` still removes the View and its owned resources, but does not separately disconnect adapter-managed contents when attachment monitoring is disabled. An application rendering Lit into an attached root with monitoring disabled must notify `notifyDetach(el)` when releasing that root. `detachContents(el)` remains the operation for physically emptying an element. ## Using the default API The native adapter is exported for direct use and for restoring native methods inside a customized class: ```javascript import { DomApi, View } from 'marionette'; const NativeView = View.extend(); NativeView.setDomApi(DomApi); ``` ## Providing a custom API The root `setDomApi` function overlays methods for `View`, `CollectionView`, and `Region`: ```javascript import { setDomApi } from 'marionette'; import MyDomApi from './my-dom-api.js'; setDomApi(MyDomApi); ``` Use a class setter when only one class or subclass needs the override. The setter creates a shallow adapter overlay for that class, so a partial override retains every other currently configured method. The current adapter and supplied overlay contribute own enumerable string and symbol properties. Inherited and non-enumerable properties are ignored. ```javascript import { View } from 'marionette'; export const PlainTextView = View.extend({ template() { return 'Literal markup'; } }); PlainTextView.setDomApi({ setContents(el, html) { el.textContent = html; } }); export function renderPlainText() { const view = new PlainTextView(); view.render(); return view; } ``` `PlainTextView` uses the custom `setContents`, while `View` and unrelated View subclasses retain their existing adapters. `CollectionView`, `Region`, and `View` each support this class-level pattern. ## Optional jQuery adapter Applications that rely on jQuery DOM bookkeeping can install jQuery and opt in at application boot: ```javascript import { setDomApi } from 'marionette'; import JQueryDomApi from '@mnjs/adapters/dom/jquery'; setDomApi(JQueryDomApi); ``` The optional adapter overrides `findEl`, `detachEl`, `setContents`, `appendContents`, and `detachContents`. `View#$()` consequently returns a jQuery collection. If application code also needs `$el`, initialize it once: ```javascript import $ from 'jquery'; import { View } from 'marionette'; const JQueryView = View.extend({ initialize() { this.$el = $(this.el); } }); ``` The root is fixed at construction, so the wrapper remains valid through rendering and detach/reattach. CollectionViews and Behaviors can initialize `$el` the same way. `$el` is application-owned; the adapter has no wrapper or View setup API. The native adapter does not create `$el`. The jQuery adapter does not replace Marionette's event delegator, restore Backbone.View inheritance, or allow selector strings as a View `el`. Configure those concerns separately when an application actually requires them. Prefer the native adapter for new applications. Use `@mnjs/adapters/dom/jquery` only for an existing integration that depends on jQuery selection, content, or detach semantics. [Canonical source](/docs/markdown/docs/dom.api.md) · [Source identity](/docs/manifest.json) --- Document: docs/dom.prerendered.md Canonical URL: https://marionettejs.com/docs/prerendered-dom/ Markdown URL: https://marionettejs.com/docs/prerendered-dom.md Reading SHA-256: ebfe3cf3e97c1abb6295c03ad8734f9eba845c026e6fed9a548a64a42c34c2fd # Prerendered Content View classes can be initialized with pre-rendered DOM. This can be HTML that's currently in the DOM: ```javascript import { View } from 'marionette'; const myView = new View({ el: document.querySelector('#foo-selector') }); myView.isRendered(); // true if '#foo-selector' exists and has content myView.isAttached(); // true if '#foo-selector' is in the DOM ``` Or it can be DOM created in memory: ```javascript import { View } from 'marionette'; const inMemoryHtml = document.createElement('div'); inMemoryHtml.textContent = 'Hello World!'; const myView = new View({ el: inMemoryHtml }); ``` In both of the cases at instantiation the view will determine its state as to whether the el is rendered or attached. **Note** `render` and `attach` events will not fire for the initial state as the state is set already at instantiation and is not changing. ## Managing `View` children With `View`, the `render` event is usually the best place to show child views for efficient nested rendering. However with pre-rendered DOM you may need to show child views in `initialize` as the view will already be rendered. ```javascript import { View } from 'marionette'; import HeaderView from './header-view'; const MyBaseLayout = View.extend({ regions: { header: '#header-region', content: '#content-region' }, el() { return document.querySelector('#base-layout'); }, initialize() { this.showChildView('header', new HeaderView()); } }); ``` ### Managing a Preexisting View Tree It may be the case that you need child views of already existing DOM as well. Query the existing DOM for each child's element. A Region declared with a selector may still hold that selector in `region.el` before its first show; `getRegion()` does not resolve it. Query from the owning View's concrete `el`: The page contains this existing markup before the module runs: ```html

Existing account

``` ```javascript import { View } from 'marionette'; export const HeaderView = View.extend({ tagName: 'header', template: () => '

Account

' }); export const BaseLayout = View.extend({ regions: { header: '#header-region', content: '#content-region' }, el() { return document.querySelector('#base-layout'); }, initialize() { this.showChildView('header', new HeaderView({ el: this.el.querySelector('#header-region').firstElementChild })); } }); export const layout = new BaseLayout(); ``` The child owns the existing `header` element. Its existing content is retained when shown because it is already rendered. Destroying the layout destroys its child and removes the owned tree. The [fixture](/docs/source/test/fixtures/docs-prerendered-content/validate.mjs) checks element identity, retained content, parent ownership, and cleanup. The same can be done with `CollectionView`. This fragment assumes an existing `#base-table` with a `tbody` containing one row per item, in source order. Supply the application's `someCollection` and configure its DataApi before construction when using an observable collection: ```javascript import { CollectionView } from 'marionette'; import RowView from './row-view'; const MyList = CollectionView.extend({ el() { return document.querySelector('#base-table'); }, childView: RowView, childViewContainer: 'tbody', buildChildView(model, ChildView, childViewOptions) { const index = this.Data.models(this.collection).indexOf(model); const childEl = this.el.querySelector('tbody').children[index]; return new ChildView({ model, ...childViewOptions, el: childEl }); } }); const myList = new MyList({ collection: someCollection }); // Unlike `View`, `CollectionView` should be rendered to build the `children` myList.render(); ``` ## Re-rendering children of a view with preexisting DOM You may be instantiating a `View` with existing HTML, but if you re-render the view, like any other view, your view will render the `template` into the view's `el` and any children will need to be re-shown. So your view will need to be prepared to handle both scenarios. ```javascript import { View } from 'marionette'; import HeaderView from './header-view'; const MyBaseLayout = View.extend({ regions: { header: '#header-region', content: '#content-region' }, el() { return document.querySelector('#base-layout'); }, initialize() { this.showChildView('header', new HeaderView({ el: this.el.querySelector('#header-region').firstElementChild })); }, template: () => '
', onRender() { this.showChildView('header', new HeaderView()); } }); ``` [Canonical source](/docs/markdown/docs/dom.prerendered.md) · [Source identity](/docs/manifest.json) --- Document: docs/optional-backbone.md Canonical URL: https://marionettejs.com/docs/backbone/ Markdown URL: https://marionettejs.com/docs/backbone.md Reading SHA-256: f97ac7f920d23916d365f3ff1dac27c3cbcb45668fdd020af13cddcb1dc81b83 # Optional Backbone Use Backbone models and collections with Marionette by installing the separate adapters package and selecting its Backbone integration. Marionette core does not import Backbone. Backbone models and collections are observable sources; “optional” means Marionette does not require that provider. Plain objects and arrays use the default [Data API](/docs/data-api.md) as static data. ```sh npm install @mnjs/adapters@5.0.0-beta.1 backbone ``` ```javascript import BackboneApi from '@mnjs/adapters/backbone'; import { setDataApi } from 'marionette'; setDataApi(BackboneApi); ``` If a Marionette owner also uses a Backbone source for `state` or `createState()`, select the StateApi role separately: ```javascript import BackboneApi from '@mnjs/adapters/backbone'; import { setStateApi } from 'marionette'; setStateApi(BackboneApi); ``` Configure `BackboneApi` once at application boot before constructing Marionette consumers or registering their subscriptions. Existing Backbone sources can be passed in; the adapter does not alter their construction or native events. For an isolated runtime, call that runtime's `setDataApi()` and `setStateApi()` methods instead of the root setters. ## What the integration does The integration supplies one combined adapter object for two related contracts: 1. As a DataApi adapter, it translates Backbone data and structural collection events. 2. As a StateApi adapter, it subscribes to Backbone state events while leaving owned Backbone state caller-controlled. The data adapter maps: | Marionette operation | Backbone source | | --- | --- | | model identity | `model.cid` | | named value read | `model.get(attribute)` | | value presence and serialization | `model.attributes` | | ordered model snapshot | `collection.models` | | application entity events | `entity.on(...)` and `entity.off(...)` | | structural observations | `sort`, `reset`, and `update` collection events | Backbone's `sort`, `reset`, and `update` payloads are translated to the neutral records documented by [`DataApi.observeCollection()`](/docs/data-api.md#collection-observations). Those Backbone-specific shapes do not enter Marionette core. As in Marionette v4, child `modelEvents` control rendering after model changes. For example, `modelEvents: { change: 'render' }` renders a child when its model changes. Collection merges still sort and filter children, but do not request another render. Backbone also reports unchanged models as merged, so treating every merge as a render request would redraw unchanged children. Sort handling follows Marionette v4: the adapter skips `sort` events carrying `add`, `remove`, or `merge` flags and handles those mutations through `update`. Explicit `collection.sort()` calls still notify the View. The observer does not retain or scan a separate membership snapshot to distinguish these events. This retains a v4 limitation: without a comparator, `collection.set()` that only reorders existing model instances emits a flagged `sort` but no `update`, so it does not automatically reorder the displayed children. Call the CollectionView's `render()` to refresh them after that operation. The original Backbone model or collection remains the value stored on a View and passed to callbacks. The integration does not wrap entities or allocate a second model graph. ## Native event identity and load order The integration uses Backbone's native `on()`, `off()`, `listenTo()`, and `stopListening()` behavior. It does not modify the Backbone namespace, constructors, prototypes, or event stores, and it does not add `triggerMethod`. Listeners registered before adapter configuration continue to work afterward: ```javascript import BackboneApi from '@mnjs/adapters/backbone'; import Backbone from 'backbone'; import { setDataApi } from 'marionette'; const model = new Backbone.Model(); const onChange = () => console.log('Model changed'); model.on('change', onChange); setDataApi(BackboneApi); model.set('ready', true); // onChange still runs ``` Destroying a Marionette owner unsubscribes its adapter-managed event handlers. The adapter leaves an owned Backbone state source and its caller-owned listeners intact because Backbone has no source-wide disposal operation that can preserve them. It does not call `stopListening()`, `off()`, or persistence-capable `Backbone.Model#destroy()` on that source. ## Applications without Backbone Do not install or import Backbone solely for Marionette. Plain models and arrays work with the default DataApi: ```javascript const model = { name: 'one' }; const collection = [model, { name: 'two' }]; ``` For observable data, use [Choosing integrations](/docs/choosing-integrations.md) to select an existing integration first. If the application requires a custom integration, implement the [DataApi contract](/docs/data-api.md) rather than manufacturing Backbone-shaped `cid`, `attributes`, `models`, or event payloads. [Canonical source](/docs/markdown/docs/optional-backbone.md) · [Source identity](/docs/manifest.json) --- Document: docs/runtime-isolation.md Canonical URL: https://marionettejs.com/docs/runtime-isolation/ Markdown URL: https://marionettejs.com/docs/runtime-isolation.md Reading SHA-256: 6634e384f01d333f27f08e0b4e9829b83eeeea3f9218806d0a2f055e8ec97582 # Runtime isolation Use named imports from `marionette` when the application shares one configuration. These exports belong to the default runtime: ```javascript import { View, Radio, setRenderer } from 'marionette'; ``` `createMarionette()` creates an isolated runtime for applications that need more than one Marionette configuration in the same JavaScript process. This configuration fragment assumes the application supplies the two renderers and templates: ```javascript import { createMarionette } from 'marionette'; const admin = createMarionette(); const storefront = createMarionette(); admin.setRenderer(adminRenderer); storefront.setRenderer(storefrontRenderer); const AdminView = admin.View.extend({ template: adminTemplate }); const StorefrontView = storefront.View.extend({ template: storefrontTemplate }); ``` Each call returns its own `Application`, `Behavior`, `CollectionView`, `MnObject`, `Region`, and `View` classes. It also owns independent `DataApi`, `DomApi`, `StateApi`, EventDelegator configuration, renderer configuration, and `Radio` channel registry. Changing one runtime does not change the default runtime or another isolated runtime. New runtimes start from Marionette's built-in adapter and renderer defaults, not from later configuration applied to the default runtime. Apply shared application configuration explicitly to each runtime that needs it. Implicit composition stays inside the selected runtime. Declarative Regions, CollectionView's empty Region, and Application's root Region use the owning runtime's classes. A Region or child Application from another runtime is rejected as an ownership conflict; construct it from the receiver's runtime instead. Isolation controls implicit class composition and mutable runtime configuration. It is not a security boundary: explicitly showing a View-like object from another runtime remains allowed under the existing Region and CollectionView display contracts. The factory is optional. Calling it does not replace the default exports, and ordinary imports do not create a runtime per View or Application instance. Class-level setters remain subclass-local within either form. Configure object-style adapters against the selected runtime's setters. For example, pass the `@mnjs/adapters/dom/jquery` export to `isolated.setDomApi()`. Likewise, pass the `@mnjs/adapters/backbone` export to the isolated runtime's `setDataApi()` and `setStateApi()` methods when it consumes Backbone data or state. No implicit adapter configuration crosses runtime boundaries. ## Configuration method contract Configure a runtime or subclass before creating its instances. The setters run synchronously; they do not render Views or replace existing event subscriptions. Changing a class prototype during a live feature is not a coordinated migration of the feature's adapters or resources. | Setter | Classes configured by the root or runtime function | Update | | --- | --- | --- | | `setDataApi(api)` | `View`, `CollectionView` | Overlays own enumerable methods on each class's current DataApi. | | `setDomApi(api)` | `View`, `CollectionView`, `Region` | Overlays own enumerable methods on each class's current DomApi. | | `setStateApi(api)` | `Application`, `Behavior`, `CollectionView`, `MnObject`, `View` | Overlays own enumerable methods on each class's current StateApi. | | `setRenderer(renderer)` | `View`, `CollectionView` | Replaces template evaluation with the supplied function. | | `setEventDelegator(delegator)` | `Behavior`, `CollectionView`, `View` | Replaces the delegator with an object exposing `delegate(options)` that returns the cleanup function for that registration. | Root and runtime setter functions return `undefined`. Corresponding class methods, such as `CustomView.setDataApi(api)`, return that class and configure its prototype. Subclasses inherit configuration until they receive their own override. An existing subclass override is not overwritten by subsequently configuring its parent class. Omitting an argument is not a reset operation. In particular, object API setters retain the current overlay, while `setRenderer(undefined)` removes the configured renderer rather than restoring the default. Use a fresh `createMarionette()` when a new independent configuration should start from built-in defaults. [Canonical source](/docs/markdown/docs/runtime-isolation.md) · [Source identity](/docs/manifest.json) --- Document: packages/data/readme.md Canonical URL: https://marionettejs.com/docs/data-package/ Markdown URL: https://marionettejs.com/docs/data-package.md Reading SHA-256: 19a48f6d3c5a5ff03415389c444f98e89cef4419370de4ee04d9b4c27d89be7d # @mnjs/data Dependency-light observable `Model` and ordered `Collection` sources for Marionette v5. The package depends only on `@mnjs/utils`; models and collections can run without core or a DOM. Install `@mnjs/data` on its own for standalone use. To use it with Marionette views, install both packages and configure the runtime before creating owners: ```sh npm install marionette@5.0.0-beta.1 @mnjs/data@5.0.0-beta.1 ``` ```js import { CollectionView, View } from 'marionette'; import { Collection, DataApi } from '@mnjs/data'; const Row = View.extend({ tagName: 'li', template: () => '', modelEvents: { change: 'render' }, onRender() { this.el.querySelector('span').textContent = this.model.get('label'); } }); const List = CollectionView.extend({ tagName: 'ul', childView: Row }); Row.setDataApi(DataApi); List.setDataApi(DataApi); const collection = new Collection([{ id: 1, label: 'one' }]); const view = new List({ collection }).render(); ``` This setup selects data for the list and its child Views. State remains an independent choice. If a View also uses a `Model` as observable state, configure StateApi on that class before construction. In the example above, place this optional setup before `new List(...)`, which constructs its children when rendered: ```js import { StateApi } from '@mnjs/data'; Row.setStateApi(StateApi); ``` Supply an existing `Model` through `state`, or return an owned one from `createState()`. Declare `stateEvents` only for the changes the owner needs to observe; the model's event names and payloads remain its own contract. Use top-level setters when all affected classes intentionally share the same provider. Configure an existing isolated runtime through its corresponding setters when needed; using this package does not require creating a new runtime. `Collection` reports synchronous `kind: 'update'`, `kind: 'reorder'`, and `kind: 'reset'` records through `DataApi.observeCollection()`. `Model` and `Collection` expose `on()`, `once()`, `off()`, `trigger()`, and `triggerMethod()` for Marionette entity event maps. `DataApi.models(collection)` returns the current ordered model snapshot. `Model` provides `get`, `has`, `set`, `unset`, `clear`, `reset`, `toObject`, and `destroy`. `Collection` provides ordered `at`, `get`, `indexOf`, iteration, `forEach`, `map`, `add`, `remove`, `reset`, `move`, `sort`, `toArray`, and `destroy` operations. Pass `{ silent: true }` to a structural mutation to suppress its normalized record and entity events. `destroy()` is the exception and always emits its destruction event. Define subclass `defaults` on the prototype, for example with `Model.extend`, a prototype method, or a prototype getter. Native class fields initialize after `super()` returns, so a `defaults = { ... }` field cannot seed construction. `move(modelOrId, index)` changes list order without removing and re-adding a model. This supports drag ordering while retaining child Views and their local state. Both `move` and `sort` emit `sort`, translated to a DataApi reorder record. Update model attributes with `model.set()` and subscribe through `modelEvents` when a child should render after a change. The native DataApi uses each model's stable `cid` as its key. Application ids may change; Collection lookup reads the current ids. Duplicate instances or ids are rejected before a reset changes membership; `add` ignores an instance or id already present. Applications should keep ids unique when changing them. `get`, `remove`, and `move` resolve an exact member instance first, then an application id, then a cid. This precedence does not change when models move. Bulk removal resolves its inputs against one current membership snapshot, including silent id changes. It skips missing identities and repeated matches, returns removed Models in input order, and keeps surviving Models in collection order. If id writes temporarily create duplicates, id lookup selects the first current member; applications should restore unique ids. Supplied native Model instances retain their identity, attributes, and subclass, including when the Collection has a different `model` constructor. That constructor is used only for raw attribute objects. Initial model instances do not configure the constructor used for future raw additions. A model may belong to multiple Collections. Its `destroy` event removes it from each containing Collection, forwarding removal options such as `silent`. The destroy event itself still fires. Destroying a Collection releases subscriptions; it does not destroy its models. `model.toObject()` returns a shallow attribute copy. `collection.toArray()` returns an array of those plain objects; use `collection.models.slice()` or iteration for model instances. Template serialization reads `model.attributes` independently of these conversion methods. There is no automatic `toJSON` hook: to serialize the plain data, use `JSON.stringify(model.toObject())` or `JSON.stringify(collection.toArray())`. Collection observation uses ordinary synchronous `update`, `reset`, and `sort` events. Notifications are not combined or replayed. Complete one structural mutation before starting another: schedule mutations from collection listeners or child lifecycle handlers after the current notification returns. Errors in listeners propagate and abort delivery, as with ordinary model events. The package does not provide persistence, REST synchronization, validation, or implicit Backbone compatibility. ## Mutation semantics `set` compares values with `Object.is`: a fresh object is a change even when its contents match, while mutating a nested object in place is not observed. `has` tests own-property presence, including a present `undefined` or `null` value. Supplied attributes override defaults, including when their value is `undefined`. Model `reset` reapplies defaults and removes attributes absent from the result. Change callbacks receive `options.changed` and `options.previous`, sparse maps for that mutation. For an attribute reported in `changed`, an absent own key in `previous` means it did not exist before the mutation; an own key with value `undefined` means it existed with that value. `previous` is not a complete model snapshot. Removing an attribute reports `undefined` in `changed`; use `has` to check its current presence. Nested Model writes complete synchronously as independent changes. Use the event's `options.changed` to inspect that event: `model.changed` reflects the latest write, which may be a nested mutation by the time an outer change callback runs. Silent writes still update attributes and `changed`; no-op writes clear `changed`. Collection `add` and `remove` events originate on the Collection. Model events are forwarded by containing Collections. Sorting is explicit: a prototype comparator is used by `sort()`, but `add` and `reset` do not automatically sort. There is no `Collection.set()` merge/reconcile operation; update retained Models explicitly when refreshing a list whose child Views must retain local state. `reset` is the deliberately destructive whole-list operation for CollectionView child Views; the Collection retains supplied Model instances rather than destroying them. ## TypeScript The package includes ESM and CommonJS declarations and a TypeScript 4.6-compatible entry. `Model.extend` and `Collection.extend` retain added methods, descendants, static replacements, and their normal attribute/model constructor inference. Event registration accepts typed callbacks and maps; event names do not validate payload types. A borrowed `triggerMethod` requires a receiver with a callable `trigger` method. A custom constructor must initialize the receiver itself. An explicit object return describes a replacement instance; an unknown result stays unknown. To return the initialized receiver while preserving methods added by descendants, state that contract explicitly: ```ts import { Model } from '@mnjs/data'; const Named = Model.extend({ constructor: function( this: Receiver, attributes: { label: string } ): Receiver { Model.call(this, attributes); return this; }, label() { return String(this.get('label')); } }); ``` The same form works with `Collection`. A constructor declared to return `void` or a primitive declares ordinary construction; the caller is responsible for honoring that declaration. TypeScript's `void` return erasure can hide an object return, so the declarations cannot prove that contract from arbitrary constructor implementations. An inferred fixed receiver return does not promise methods added by later descendants. Direct native subclasses remain supported. Calling their inherited `.extend()` without an explicit constructor is rejected because that path calls the parent with `apply`, which cannot invoke a native class. An explicit constructor skips that forwarding path and owns its initialization or replacement result. TypeScript 4.6 narrows `instanceof` checks for the root constructors and ordinary method-only extensions. Its callable-intersection limitation prevents that narrowing on extensions with custom statics; directly constructed instances and those static members remain typed. Collection member types include both supplied Models and the constructor used for raw attributes. Constructor `options.model` replaces a prototype `model` factory; without either, raw attributes construct a base Model. Narrow an item with `instanceof ModelClass` before using subclass-specific methods. The instance `model` constructor has the same conservative member result type. Model attributes and `toObject()` are partial: construction, `unset`, and `clear` can leave any attribute absent. Known string keys in `set(key, value)` use the same attribute value types as object-form writes; arbitrary dynamic keys remain open. An explicitly typed Collection also checks raw attribute inputs against its model attribute shape. These are compile-time contracts, not runtime validation. [Canonical source](/docs/markdown/packages/data/readme.md) · [Source identity](/docs/manifest.json) --- Document: packages/adapters/readme.md Canonical URL: https://marionettejs.com/docs/adapters-package/ Markdown URL: https://marionettejs.com/docs/adapters-package.md Reading SHA-256: f26799985f8ac3ebab251a237ea840f876378dd0a8547667731b1948a8c5d4c5 # @mnjs/adapters First-party optional integrations for Marionette v5. The package intentionally has no root export: import only the adapter and optional peer your application uses. Installing this package does not install every provider. The adapters have separate module graphs and no import-time installation; unused integrations stay out of the application bundle. Source is grouped into `data` and `dom`, while each integration remains an explicit package subpath. ## Adapter conventions - `SomethingApi` is an object implementing an existing runtime contract. - `createSomethingApi(options)` returns that object when configuration is required. Imports do not configure Marionette. Use the existing `setDomApi`, `setDataApi`, and `setStateApi` methods before constructing instances. An integration may satisfy more than one contract: Backbone uses the same adapter object for both data and state. Setters overlay supplied methods; the last supplied version of a method wins. Configure content rendering after general DOM operations. Adapters use public APIs and document source ownership and cleanup below. Template evaluation is a function configured with `View.setRenderer()`. Projects can supply that function directly; it does not need a packaged adapter. Lit and Morphdom belong to DomApi because they apply template results to the DOM. ## Backbone ```sh npm install marionette@5.0.0-beta.1 @mnjs/adapters@5.0.0-beta.1 backbone ``` Configure DataApi before creating Views that consume Backbone models or collections. The example below covers model-backed Views. For a CollectionView, configure DataApi on both its parent CollectionView class and its child View class before construction. For a feature-specific integration, configure its View subclass: ```js import BackboneApi from '@mnjs/adapters/backbone'; import { View } from 'marionette'; const BackboneView = View.extend(); BackboneView.setDataApi(BackboneApi); ``` Use the top-level `setDataApi(BackboneApi)` when the whole application shares that data provider. Configure StateApi separately, only for owners whose state uses Backbone and needs subscriptions or owned-source cleanup: ```js BackboneView.setStateApi(BackboneApi); ``` The same adapter object can configure other state-owning classes, or the corresponding setters on an existing isolated runtime. Choosing Backbone data does not require choosing Backbone state or creating an isolated runtime. The adapter uses Backbone's native events and does not modify Backbone objects or prototypes. Releasing an owned Backbone state source removes only the adapter-managed owner subscriptions. The adapter leaves the source and its caller-owned listeners intact; it does not call source-wide `stopListening()`, `off()`, or persistence-capable `Model#destroy()` methods. ## XState actors Use the XState actor adapter when a parent actor snapshot contains stable child actor references. Actor-reference identity associates each child actor with its View; stopping and respawning an actor creates a different model identity even when the actors share an `id`. The adapter supports XState `^5.32.6`. ```sh npm install marionette@5.0.0-beta.1 @mnjs/adapters@5.0.0-beta.1 xstate ``` This configuration fragment assumes an application-owned `parentActor` whose `context.children` contains stable child actor references. Create and start the actors in the application's XState setup. ```js import createXStateActorApi from '@mnjs/adapters/xstate'; import { CollectionView, View } from 'marionette'; const XStateActorApi = createXStateActorApi({ select: snapshot => snapshot.context.children, snapshotEvent: 'actor:snapshot' }); const ActorView = View.extend({ template: context => context.label, modelEvents: { 'actor:snapshot': 'render', announced: 'onAnnounced' }, onAnnounced(event) { console.log(event.label); } }); const ActorList = CollectionView.extend({ childView: ActorView }); ActorView.setDataApi(XStateActorApi); ActorList.setDataApi(XStateActorApi); const view = new ActorList({ collection: parentActor }).render(); ``` For a CollectionView, the required selector receives the parent actor's synchronous snapshot and returns its ordered child actor references. Omit `select` when configuring only actor models or state. Templates receive each child actor's current `snapshot.context`. Configure `snapshotEvent` only when declarative `modelEvents` or `stateEvents` should observe actor snapshots; the chosen name is reserved by that adapter instance. Every other event-map name is passed to `actor.on()` and therefore observes an explicitly emitted actor event, not an event sent to the actor. Subscribing to an already-started actor does not replay its current snapshot, so initial rendering reads `getSnapshot()` directly. Replace the selected array when membership or order changes. Reusing an unchanged array lets the adapter skip comparison; a newly allocated array requires a keyed scan per observer, even if its contents are identical. Supplied parent, child, and state actors are borrowed. Destroying a Marionette owner releases its subscriptions and Views but does not stop those actors. An actor returned by an owner's `createState()` factory is owned; after releasing its subscriptions, Marionette calls this adapter's `disposeOwned()` and stops that actor. The keyed snapshot helper is private implementation only; there is no generic snapshot-source package export. ## jQuery DomApi ```sh npm install marionette@5.0.0-beta.1 @mnjs/adapters@5.0.0-beta.1 jquery ``` ```js import { View } from 'marionette'; import JQueryDomApi from '@mnjs/adapters/dom/jquery'; const JQueryView = View.extend(); JQueryView.setDomApi(JQueryDomApi); ``` If application code needs `$el`, initialize it once: ```js import $ from 'jquery'; import { View } from 'marionette'; import JQueryDomApi from '@mnjs/adapters/dom/jquery'; const JQueryView = View.extend({ initialize() { this.$el = $(this.el); } }); JQueryView.setDomApi(JQueryDomApi); ``` Views, CollectionViews, and Behaviors keep their initial root. The application owns `$el`; no wrapper helper or extra package subpath is needed. Importing an adapter subpath does not load any other adapter or optional peer. ## DOM contents The Morphdom and Lit DOM adapters update a View's contents synchronously and keep its `el` in place. Marionette still owns View events, attachment, destruction, and Regions. A parent render still destroys its Region children before updating the parent template; incremental rendering does not preserve those child Views. Keep Region placeholders empty in your templates so the adapter and Region do not both manage the same contents. Configure these adapters through `ViewClass.setDomApi(adapter)` before creating instances. The adapter overlays only its supplied methods, so unrelated DOM operations remain in place. Configure jQuery first if you need its query and attachment operations alongside Morphdom or Lit. ### Morphdom ```sh npm install marionette@5.0.0-beta.1 @mnjs/adapters@5.0.0-beta.1 morphdom ``` ```js import { View } from 'marionette'; import MorphdomDomApi from '@mnjs/adapters/dom/morphdom'; const MessageView = View.extend({ template: () => '

Hello again.

' }); MessageView.setDomApi(MorphdomDomApi); ``` The template returns an HTML string containing the View's contents. Morphdom matches children using its normal rules, including element IDs. The adapter installs HTML directly into an empty root and morphs existing contents using `childrenOnly`, leaving the root's attributes under Marionette's control. Use `renderAttributes()` to refresh those attributes. ### Lit HTML ```sh npm install marionette@5.0.0-beta.1 @mnjs/adapters@5.0.0-beta.1 lit-html ``` ```js 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); ``` Configure a View subclass before creating its instances. Further subclasses inherit the adapter. Neither DOM adapter modifies View methods or needs a View reference: template evaluation stays in the renderer and the returned value goes to `Dom.setContents(el, value)`. Lit async directives can own subscriptions and other resources. Marionette calls `Dom.notifyAttach(el)` and `Dom.notifyDetach(el)` through its existing attachment monitoring. Lit translates these notifications to its directive connection API. Detaching and destroying a View disconnects its directives while preserving the View root. A View keeps its initial element for its lifetime. Destroying an already constructed View also disconnects resources created before an explicit render failed. Failed construction does not roll back initialization. Keep `monitorViewEvents` enabled on the View and its ancestors and manage attachment through Regions. If you disable monitoring or remove its handlers with `off()`, the application must call the adapter's attachment methods itself. There is no separate hidden cleanup listener. Lifecycle overrides must call parent methods, as with other Marionette lifecycle overrides. The first explicit render replaces preexisting contents; this is not hydration. Subsequent renders update Lit's marked range. A disconnected element can be adopted by another View using the same adapter without erasing its contents. Release the previous owner first; one element cannot have two active View owners. Lit event handlers use Lit's normal element receiver; use closures when a handler needs application or View state. Do not independently replace Lit's contents or switch content adapters after rendering. [Canonical source](/docs/markdown/packages/adapters/readme.md) · [Source identity](/docs/manifest.json)