Start here

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

Common Marionette Concepts#

Learn the configuration patterns once, then use them across Marionette's classes. Each class's reference explains when it reads an option and whether it reads it again. For checked application options, see the TypeScript example.

Documentation Index#

Importing Marionette#

Install the v5 marionette package and use named imports:

import { Application, View } from 'marionette';

const view = new View();
const app = new Application();

V5 has no default namespace export. The separate @mnjs/adapters package provides optional integration subpaths; see Installing Marionette for the entrypoints and their dependencies.

Existing no-bundler applications may serve the published dist/marionette.umd.js artifact. It exposes the named API on the global Marionette object and supports Marionette.noConflict(). Package-based named imports are the canonical path for new applications.

Class-based Inheritance#

Like Backbone, Marionette provides a pseudo-class extend method. All built-in classes, such as View and MnObject, provide this method.

The protoProps and staticProps hashes passed to extend contribute their own enumerable string and symbol keys. Non-enumerable and inherited input properties are ignored, except that an own constructor selects the child constructor even when it is non-enumerable. Enumerable string statics from the parent, including inherited ones, are copied to the child constructor.

In the example below, we create a new pseudo-class called MyView:

import { View } from 'marionette';

const MyView = View.extend({});

You can now create instances of MyView with JavaScript's new keyword:

const view = new MyView();

Value Attributes#

When we extend classes, we can provide class attributes with specific values by defining them in the object we pass as the extend parameter:

import { View } from 'marionette';

const MyView = View.extend({
  className: 'bg-success',

  template: () => '<div class="my-region"></div>',

  regions: {
    myRegion: '.my-region'
  },

  modelEvents: {
    change: 'removeBackground'
  },

  removeBackground() {
    this.el.classList.remove('bg-success');
  }
});

When MyView creates its element, the element receives the bg-success class. When the View renders, the myRegion Region targets .my-region within that element. Entity-event behavior is documented separately because it depends on an attached entity.

Functions Returning Values#

Many configuration attributes accept either a value or a function returning that value. Attributes documented as value callbacks call the function with the Marionette instance as this. A template function is the renderer itself and instead receives serialized data as its argument; it does not receive the View as this. Resolution timing is part of each attribute's contract; do not assume every function runs during construction or that every result is cached for the object's lifetime.

import { View } from 'marionette';

let cancelCalls = 0;
let defaultCalls = 0;
let overrideCalls = 0;
let templateContext;
let templateData;

const MyView = View.extend({
  options() {
    this.optionsResolutionCount = (this.optionsResolutionCount || 0) + 1;
    return {
      count: 1,
      enabled: true,
      label: 'default',
      tone: 'quiet'
    };
  },

  className() {
    this.classNameResolutionCount = (this.classNameResolutionCount || 0) + 1;
    return `notice-${this.getOption('tone')}`;
  },

  template(data) {
    templateContext = this;
    templateData = data;
    return '<button class="save">Save</button><button class="cancel">Cancel</button>';
  },

  triggers: {
    'click .cancel': 'cancel:default',
    'click .save': 'save:default'
  },
});

const view = new MyView({
  count: 0,
  enabled: false,
  label: null,
  tone: 'urgent',
  triggers: {
    'click .save': 'save:override'
  },
});

const classNameBeforeRender = view.el.className;

view.on('cancel:default', () => {
  cancelCalls += 1;
});

view.on('save:default', () => {
  defaultCalls += 1;
});

view.on('save:override', () => {
  overrideCalls += 1;
});

view.render();
view.el.querySelector('.save').click();
view.el.querySelector('.cancel').click();

export {
  cancelCalls,
  classNameBeforeRender,
  defaultCalls,
  overrideCalls,
  templateContext,
  templateData,
  view
};

Here options() supplies class defaults, the constructor's tone wins, and className() resolves while the View creates its element. The constructor's triggers map replaces the class map rather than merging with it.

Function Context#

Use a normal method when a configuration callback needs the instance context. An arrow function retains its surrounding lexical this, so it is appropriate only when the callback does not need the Marionette instance.

Binding Attributes on Instantiation#

The documented constructor options for each class can replace matching values defined on its prototype. This supports runtime configuration such as a View's events, triggers, model, collection, and Region definitions:

import { View } from 'marionette';

const MyView = View.extend({
  template: () => '<a href="#details">Details</a>'
});

const myView = new MyView({
  triggers: {
    'click a': 'show:link'
  }
});

This will set a trigger called show:link that will be fired whenever the user clicks an <a> inside the view.

Constructor values replace matching class values; map options are not implicitly deep-merged. For example:

import { View } from 'marionette';

const MyView = View.extend({
  template: () => '<button class="save">Save</button><a href="#details">Details</a>',

  triggers: {
    'click @ui.save': 'save:form'
  }
});

const myView = new MyView({
  triggers: {
    'click a': 'show:link'
  }
});

In this example, show:link is the only configured trigger. The constructor's triggers object completely replaces the class-defined object.

Setting Options#

Every Marionette class stores its merged class defaults and constructor values on this.options. getOption(name) reads a defined value from this.options before falling back to the instance. A constructor value of false, null, or 0 therefore remains an intentional override; only undefined falls through.

Resolved class defaults and constructor option hashes contribute their own enumerable string and symbol properties when Marionette builds options. Inherited and non-enumerable properties are ignored. mergeOptions copies only the requested own enumerable string options onto an instance.

import { View } from 'marionette';

const MyView = View.extend({
  checkOption() {
    console.log(this.getOption('foo'));
  }
});

const view = new MyView({
  foo: 'some text'
});

view.checkOption();  // prints 'some text'

Constructor/default option merges use own enumerable string and symbol properties. See getOption and mergeOptions for the exact lookup and copying boundaries.

Common Marionette Functionality#

Marionette has a few methods and core functionality that are common to all classes.

Continue Reading....