5.0.0-beta.1 · Published beta · b06750c5. Published on npm. Match your installed version.
ConsumerCommon Marionette Functionality#
Marionette classes share a small set of lifecycle, event, request, and option helpers.
Documentation Index#
- Shared helpers
- initialize
- extend
- Events API
- triggerMethod
- bindEvents
- unbindEvents
- bindRequests
- unbindRequests
- normalizeMethods
- getOption
- mergeOptions
- The
optionsProperty
Shared helpers#
The reusable option, binding, and event helpers are also available from
@mnjs/utils for components outside Marionette's classes:
import { getOption, normalizeMethods } from '@mnjs/utils';
const component = {
options: { label: 'Inbox' },
getOption,
normalizeMethods,
onOpen() {}
};
component.getOption('label'); // 'Inbox'
component.normalizeMethods({ open: 'onOpen' });
Install @mnjs/utils directly when importing it in an application. Use the
same version as Marionette during prereleases. Core and native data depend on this
package and use the same implementations. Helpers that read this can be mixed
into a component or invoked with .call(component, ...).
initialize#
initialize is a no-op method that you can override on any Marionette class.
It is called when the class is instantiated and receives the constructor
arguments unchanged. The first argument is conventionally an options object.
Use getOption to read that object together with class defaults.
import { MnObject } from 'marionette';
const MyObject = MnObject.extend({
initialize(options, secondArgument) {
console.log(options.foo, this.getOption('foo'), secondArgument);
}
});
new MyObject({ foo: 'bar' }, 'baz'); // logs "bar" "bar" "baz"
extend#
extend is available on Marionette class definitions for
class-based inheritance.
Events API#
Marionette classes include Marionette's owned Events API. Each
class can emit events and listen to other objects that implement the compatible
event interface, including native Backbone emitters. The separate
Backbone integration selects data reads and
collection observation; the core Events API does not require Backbone.
The Events API should not be confused with view events,
which capture DOM events.
triggerMethod#
triggerMethod calls a matching method and then triggers an event on the
object. The first letter of each event-name segment is capitalized and on is
prepended:
triggerMethod('foo')callsonFooand triggersfoo.triggerMethod('before:foo')callsonBeforeFooand triggersbefore:foo.
Arguments after the event name are passed to both the method and event. The
matching method is resolved through getOption, runs first with the Marionette
object as its context, and supplies the return value of triggerMethod. If that
method throws, the event is not triggered.
import { MnObject } from 'marionette';
const MyObject = MnObject.extend({
onFoo(value) {
return value.toUpperCase();
}
});
const object = new MyObject();
object.on('foo', value => console.log(value));
object.triggerMethod('foo', 'bar'); // logs "bar" and returns "BAR"
See the Marionette events documentation for the complete event and method-handler contract.
bindEvents#
bindEvents(entity, bindings) uses the Marionette object's listenTo API to
bind events from another compatible event emitter. The binding map associates
event names with functions or method names on the listening object. The method
returns the listening object.
Marionette classes and Radio channels implement the required
event interface. Backbone models, collections, and other Backbone emitters can
participate directly through compatible on and off methods. Configure the
Backbone integration separately when a View
also needs Backbone data reads, serialization, or collection observation.
Binding maps follow the declared object contract. An own enumerable __proto__
event name is rejected with code MN0026 before any listener is added. This restriction applies only to entity-event maps;
Marionette's direct Events API supports __proto__ as an ordinary event name.
unbindEvents#
unbindEvents(entity, bindings) stops the subscriptions represented by a
binding map. Without a binding map, it stops every subscription that this
Marionette object established to that entity. It does not remove listeners
owned by other objects or direct handlers registered on the entity. The method
returns the listening object.
When selectively unbinding with a map, an own enumerable __proto__ event name
is rejected with MarionetteError code MN0026 before any listener is removed.
bindRequests#
bindRequests(channel, bindings) registers replies on a Radio
channel. The binding map associates request names with functions or method names
on the Marionette object. Reply methods run with that object as their context,
and bindRequests returns the object.
Binding maps follow the declared object contract. String-named handlers must resolve to callable methods on the receiver.
unbindRequests#
unbindRequests(channel, bindings) removes the replies represented by a
binding map. Without a binding map, it removes every reply owned by this object
from that channel. Replies owned by other objects remain registered. The method
returns the object.
Warning: Request bindings created manually retain their owner as reply context. To avoid memory leaks, call
unbindRequestsin or beforeonBeforeDestroy, and whenever a shorter binding lifetime ends.
MnObject and Application instead support the declarative channelName,
radioEvents, and radioRequests options; those owned bindings are cleaned up
when the owner is destroyed. A View using bindRequests directly should call
unbindRequests as part of its own cleanup.
The following example shows both event and request bindings remaining scoped to their owner.
import { MnObject, Radio } from 'marionette';
const source = new MnObject();
const channel = Radio.channel('common-owner-bindings');
const unrelatedMessages = [];
source.on('status', value => unrelatedMessages.push(value));
channel.reply('status:other', () => 'other');
const Owner = MnObject.extend({
initialize() {
this.messages = [];
this.bindEvents(source, { status: 'onStatus' });
this.bindRequests(channel, { 'status:current': 'getStatus' });
},
onStatus(value) {
this.messages.push(value);
},
getStatus() {
return this.messages[this.messages.length - 1];
}
});
const owner = new Owner();
source.trigger('status', 'ready');
const ownerReply = channel.request('status:current'); // "ready"
owner.unbindEvents(source);
owner.unbindRequests(channel);
source.trigger('status', 'after');
const ownerReplyAfterCleanup = channel.request('status:current'); // undefined
const unrelatedReplyAfterCleanup = channel.request('status:other'); // "other"
export {
Radio,
owner,
ownerReply,
ownerReplyAfterCleanup,
unrelatedMessages,
unrelatedReplyAfterCleanup
};
normalizeMethods#
normalizeMethods(bindings) returns a fresh map with method-name strings
replaced by function references from the Marionette object. Only the map's own
enumerable string keys are normalized; inherited, symbol, and non-enumerable
properties are ignored. A literal own __proto__ entry remains a handler key
without changing the returned object's prototype.
Every supplied handler must be a function or a string that resolves to a
callable own or inherited method on the binding context. Otherwise Marionette
throws MarionetteError with code MN0019. This invariant also applies to
event and request binding maps, including their unbind operations, and to model,
collection, Radio, and child-view event bindings.
import { View } from 'marionette';
const MyView = View.extend({
initialize() {
this.normalizedActions = this.normalizeMethods({
'action:one': 'handleActionOne',
'action:two': this.handleActionTwo
});
},
handleActionOne() {
console.log('action:one');
},
handleActionTwo() {
console.log('action:two');
}
});
getOption#
getOption(name) first reads the named value from the merged options object.
If that value is undefined, it falls back to the same property on the
instance or its prototype. Explicit option values such as null, false, 0,
and an empty string are returned without falling back. Function values are
returned without being invoked.
mergeOptions#
mergeOptions(options, keys) copies selected option values directly onto the
class instance. keys must be an array when options are present. Only requested
own enumerable string properties with values other than undefined are copied; inherited, symbol,
and non-enumerable properties are ignored.
The options Property#
A class-level options property supplies defaults. Marionette creates a fresh
this.options object for each instance by merging those defaults with the
constructor options; constructor values take precedence. The options argument
received by initialize remains the raw object supplied by the caller, so use
getOption when class defaults must be included.
mergeOptions is separate: it copies only named values directly onto the
instance for APIs that need instance properties.
import { MnObject } from 'marionette';
const service = { name: 'example' };
const Example = MnObject.extend({
enabled: true,
options: {
mode: 'default'
},
initialize(options) {
this.rawMode = options.mode;
this.mergeOptions(options, ['service']);
}
});
const example = new Example({
enabled: false,
service,
extra: 'kept only in this.options'
});
const rawMode = example.rawMode; // undefined
example.getOption('mode'); // "default"
example.getOption('enabled'); // false
example.getOption('extra'); // "kept only in this.options"
console.log(example.service === service); // true
export { example, rawMode, service };
Marionette Classes#
Marionette provides classes for building a view tree and application structure.