From d4330ec6f69c5dec811ec648b0af26f7dbf2456a Mon Sep 17 00:00:00 2001
From: Paul Falgout Some textWidgets
')
+});
+```
+
+**Errors** An error will throw if the childViewContainer can not be found.
+
+### Re-rendering the CollectionView
+
+If you need to re-render the entire collection or the template, you can call the
+`collectionView.render` method. This method will destroying all of
+the child views that may have previously been added.
+
+## View Lifecycle and Events
+
+An instantiated `CollectionView` 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](./view.lifecycle.md)
+- [View DOM Change Events](./events.class.md#dom-change-events)
+- [View Destroy Events](./events.class.md#destroy-events)
+
+## Entity Events
+
+The `CollectionView` can bind to events that occur on the attached `model` and `collection` - this
+includes both [standard backbone-events](http://backbonejs.org/#Events-catalog) and custom events.
+
+Read More:
+- [Entity Events](./events.entity.md)
+
+## DOM Interactions
+
+In addition to what Backbone provides the views, Marionette has additional API
+for DOM interactions: `events`, `triggers`, and `ui`.
+
+By default `ui` is only bound to the elements within the [template](#rendering-a-template).
+However as `events` and `triggers` are delegated to the view's `el` they will apply to any children.
+There may be instances where binding `ui` is helpful when you want to access elements inside
+`CollectionView`s children with [`getUI()`](./dom.interactions.md#accessing-ui-elements). For these
+cases you will need to bind `ui` yourself. To do so run `bindUIElements` on the `CollectionView`:
+
+```javascript
+import { CollectionView } from 'backbone.marionette';
+
+const MyCollectionView = CollectionView.extend({
+ // ...
+
+ ui: {
+ checkbox: 'input[type="checkbox"]'
+ }
});
+
+const collectionView = new MyCollectionView();
+
+collectionView.bindUIElements();
+
+console.log(collectionView.getUI('checkbox')); // Output all checkboxes.
```
-Child views must be defined before they are referenced by the
-`childView` attribute in a collection view definition.
+Read More:
+- [DOM Interactions](./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](./marionette.behavior.md#using-behaviors)
+
+## Managing Children
+
+Children are automatically managed once the `CollectionView` is
+[rendered](#rendering-a-collectionview). For each model within the
+`collection` the `CollectionView` will build and store a `childView`
+within its `children` object. This allows you to easily access
+the views within the collection view, iterate them, find them by
+a given indexer such as the view's model or id and more.
+
+After the initial `render` the `CollectionView` binds to the `update`
+and `reset` events of the `collection`.
+
+When the `collection` for the view is `reset`, the view will destroy all
+children and re-render the entire collection.
-Alternatively, you can specify a `childView` in the options for
-the constructor:
+When a model is added to the `collection`, the `CollectionView` will render that
+one model into the `children`.
+
+When a model is removed from the `collection` (or destroyed / deleted), the `CollectionView`
+will destroy and remove that model's child view.
+
+When the `collection` for the view is sorted, the view by default automatically re-sort
+its child views unless the `sortWithCollection` attribute on the `CollectionView` is set
+to `false`, or the `viewComparator` is `false`.
```javascript
-var Mn = require('backbone.marionette');
+import Backbone from 'backbone';
+import { View, CollectionView } from 'backbone.marionette';
+
+const collection = new Backbone.Collection();
+
+const MyChildView = View.extend({:
+ template: false
+});
+
+const MyCollectionView = CollectionView.extend({
+ childView: MyChildView,
+ collection,
+});
+
+const myCollectionView = new MyCollectionView();
+
+// Collection view will not re-render as it has not been rendered
+collection.reset([{foo: 'foo'}]);
+
+myCollectionView.render();
+
+// Collection view will effectively re-render displaying the new model
+collection.reset([{foo: 'bar'}]);
+```
-var MyCollectionView = Mn.CollectionView.extend({...});
+When the children are rendered the
+[`render:children` and `before:render:children` events](./events.class.md#renderchildren-and-beforerenderchildren-events)
+will trigger.
-new MyCollectionView({
+When a childview is added to the children
+[`add:child` and `before:add:child` events](./events.class.md#addchild-and-beforeaddchild-events)
+will trigger
+
+When a childview is removed from the children
+[`remove:child` and `before:remove:child` events](./events.class.md#removechild-and-beforeremovechild-events)
+will trigger.
+
+### Attaching `children` within the `el`
+
+By default the `CollectionView` will add the HTML of each ChildView
+into an element buffer array, and then call the DOM API's
+[appendContents](./dom.api.md#appendcontentsel-contents) once at the end
+to move all of the HTML within the collection view's `el`.
+
+You can override this by specifying an `attachHtml` method in your
+view definition. This method takes two parameters and has no return value.
+
+```javascript
+import { CollectionView } from 'backbone.marionette';
+
+CollectionView.extend({
+
+ // The default implementation:
+ attachHtml(els, $container){
+ // Unless childViewContainer, $container === this.$el
+ this.Dom.appendContents(this.el, els);
+ }
+});
+```
+
+The first parameter is the HTML buffer, and the second parameter
+is the expected container for the children which by default equates
+to the view's `el` unless a [`childViewContainer`](#defining-the-childViewContainer)
+is set.
+
+### Destroying All `children`
+
+`CollectionView` implements a `destroy` method which automatically
+destroys its children and cleans up listeners.
+
+When the children are destroyed the
+[`destroy:children` and `before:destroy:children` events](./events.class.md#destroychildren-and-beforedestroychildren-events)
+will trigger.
+
+Read More:
+- [View Destroy Events](./events.class.md#destroy-events)
+
+## CollectionView's `childView`
+
+When using a `collection` to manage the children of `CollectionView`,
+specify a `childView` for your `CollectionView`. This must be
+a Backbone view class definition, not an instance. It can be any
+`Backbone.View` related class including both Marionette's `View` and
+`CollectionView`.
+
+```javascript
+import { View, CollectionView } from 'backbone.marionette';
+
+const MyChildView = View.extend({});
+
+const MyCollectionView = CollectionView.extend({
childView: MyChildView
});
```
-If you do not specify a `childView`, an exception will be thrown
+**Errors** If you do not specify a `childView`, an exception will be thrown
stating that you must specify a `childView`.
You can also define `childView` as a function. In this form, the value
@@ -72,25 +310,21 @@ when a `Model` needs to be initially rendered. This method also gives you
the ability to customize per `Model` `ChildViews`.
```javascript
-var Bb = require('backbone');
-var Mn = require('backbone.marionette');
+import _ from 'underscore';
+import Backbone from 'backbone';
+import { View, CollectionView } from 'backbone.marionette';
-var FooBar = Bb.Model.extend({
- defaults: {
- isFoo: false
- }
+const FooView =View.extend({
+ template: _.template('foo')
});
-var FooView = Mn.View.extend({
- template: '#foo-template'
-});
-var BarView = Mn.View.extend({
- template: '#bar-template'
+const BarView = View.extend({
+ bar
});
-var MyCollectionView = Mn.CollectionView.extend({
- collection: new Bb.Collection(),
- childView: function(item) {
+const MyCollectionView = CollectionView.extend({
+ collection: new Backbone.Collection(),
+ childView(item) {
// Choose which view class to render,
// depending on the properties of the item model
if (item.get('isFoo')) {
@@ -102,11 +336,13 @@ var MyCollectionView = Mn.CollectionView.extend({
}
});
-var collectionView = new MyCollectionView();
-var foo = new FooBar({
+const collectionView = new MyCollectionView();
+
+const foo = new Backbone.Model({
isFoo: true
});
-var bar = new FooBar({
+
+const bar = new Backbone.Model({
isFoo: false
});
@@ -117,7 +353,63 @@ collectionView.collection.add(foo);
collectionView.collection.add(bar);
```
-### CollectionView's `childViewOptions`
+**Errors** If `childView` is a function that does not return a view class
+an error will be thrown.
+
+### Building the `children`
+
+The `buildChildView` method is responsible for taking the ChildView class and
+instantiating it with the appropriate data. This method takes three
+parameters and returns a view instance to be used as the child view.
+
+```javascript
+buildChildView(child, ChildViewClass, childViewOptions){
+ // build the final list of options for the childView class
+ const options = _.extend({model: child}, childViewOptions);
+ // create the child view instance
+ const view = new ChildViewClass(options);
+ // return it
+ return view;
+},
+```
+
+Override this method when you need a more complicated build, but use [`childView`](#collectionviews-childview)
+if you need to determine _which_ View class to instantiate.
+
+```javascript
+import _ from 'underscore';
+import Backbone from 'backbone';
+import { CollectionView } from 'backbone.marionette';
+import MyListView from './my-list-view';
+import MyView from './my-view';
+
+const MyCollectionView = CollectionView.extend({
+ childView(child) {
+ if (child.get('type') === 'list') {
+ return MyListView;
+ }
+
+ return MyView;
+ },
+ buildChildView(child, ChildViewClass, childViewOptions) {
+ const options = {};
+
+ if (child.get('type') === 'list') {
+ const childList = new Backbone.Collection(child.get('list'));
+ options = _.extend({collection: childList}, childViewOptions);
+ } else {
+ options = _.extend({model: child}, childViewOptions);
+ }
+
+ // create the child view instance
+ const view = new ChildViewClass(options);
+ // return it
+ return view;
+ }
+});
+```
+
+### Passing Data to the `childView`
There may be scenarios where you need to pass data from your parent
collection view in to each of the childView instances. To do this, provide
@@ -126,15 +418,15 @@ literal. This will be passed to the constructor of your childView as part
of the `options`.
```javascript
-var Mn = require('backbone.marionette');
+import { View, CollectionView } from 'backbone.marionette';
-var ChildView = Mn.View.extend({
- initialize: function(options) {
+const ChildView = View.extend({
+ initialize(options) {
console.log(options.foo); // => "bar"
}
});
-var CollectionView = Mn.CollectionView.extend({
+const MyCollectionView = CollectionView.extend({
childView: ChildView,
childViewOptions: {
@@ -150,14 +442,14 @@ the function should you need access to it when calculating
of the object will be copied to the `childView` instance's options.
```javascript
-var Mn = require('backbone.marionette');
+import { CollectionView } from 'backbone.marionette';
-var CollectionView = Mn.CollectionView.extend({
- childViewOptions: function(model) {
+const MyCollectionView = CollectionView.extend({
+ childViewOptions(model) {
// do some calculations based on the model
return {
foo: 'bar'
- }
+ };
}
});
```
@@ -170,13 +462,14 @@ collection view. The `emptyView` just like the [`childView`](#collectionviews-ch
function that returns the `emptyView`.
```javascript
-var Mn = require('backbone.marionette');
+import _ from 'underscore';
+import { View, CollectionView } from 'backbone.marionette';
-var MyEmptyView = Mn.View.extend({
+const MyEmptyView = View.extend({
template: _.template('Nothing to display.')
});
-var MyCollectionView = Mn.CollectionView.extend({
+const MyCollectionView = CollectionView.extend({
// ...
emptyView: MyEmptyView
@@ -196,24 +489,27 @@ Showing things in this region directly is not advised.
const isEmptyShowing = myCollectionView.getEmptyRegion().hasView();
```
-### CollectionView's `emptyViewOptions`
+This region can be useful for handling the
+[EmptyView Region Events](./events.class.md#collectionview-emptyview-region-events).
-Similar to [`childView`](#collectionviews-childview) and [`childViewOptions`](#collectionviews-childviewoptions),
+### Passing Data to the `emptyView`
+
+Similar to [`childView`](#collectionviews-childview) and [`childViewOptions`](#padding-data-to-the-childview),
there is an `emptyViewOptions` property that will be passed to the `emptyView` constructor.
It can be provided as an object literal or as a function.
If `emptyViewOptions` aren't provided the `CollectionView` will default to passing the `childViewOptions` to the `emptyView`.
```javascript
-var Mn = require('backbone.marionette');
+import { View, CollectionView } from 'backbone.marionette';
-var EmptyView = Mn.View({
- initialize: function(options){
+const EmptyView = View.extend({
+ initialize(options){
console.log(options.foo); // => "bar"
}
});
-var CollectionView = Mn.CollectionView({
+const MyCollectionView = CollectionView.extend({
emptyView: EmptyView,
emptyViewOptions: {
@@ -222,204 +518,575 @@ var CollectionView = Mn.CollectionView({
});
```
-### CollectionView's `isEmpty`
+### Defining When an `emptyView` shows
If you want to control when the empty view is rendered, you can override
`isEmpty`:
```javascript
-var Mn = require('backbone.marionette');
+import { CollectionView } from 'backbone.marionette';
-var MyCollectionView = Mn.CollectionView.extend({
- isEmpty: function(allViewsFiltered) {
+const MyCollectionView = CollectionView.extend({
+ isEmpty() {
// some logic to calculate if the view should be rendered as empty
return this.collection.length < 2;
}
});
```
-In the normal lifecycle of a `CollectionView`, `isEmpty` will be called
-twice. Once when a render begins, and again after the [`viewFilter`](#filtering) is run. For the call after filtering, a boolean will be passed indicating if all
-of the CollectionView's `children` were filtered.
+The default implementation of `isEmpty` returns `!this.children.length`.
-## CollectionView's `render`
+You can also use this method to determine when the empty view was shown:
-The `render` method of the collection view is 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 'backbone.marionette';
+
+const MyCollectionView = CollectionView.extend({
+ // ...
+ onRenderChildren() {
+ if (this.isEmpty()) { console.log('Empty View Shown'); }
+ }
+});
+```
+
+## Accessing a Child View
+
+You can retrieve a view by a number of methods. If the findBy* method cannot find the view,
+it will return `undefined`.
+
+**Note** That children represents the views rendered that are or will be
+attached within the view's `el`.
+
+#### CollectionView `children`'s: `findByCid`
+Find a view by it's cid.
```javascript
-var Mn = require('backbone.marionette');
+const bView = myCollectionView.children.findByCid(buttonView.cid);
+```
-var MyCollectionView = Mn.CollectionView.extend({...});
+#### CollectionView `children`'s: `findByModel`
+Find a view by model.
-// all of the children views will now be rendered.
-new MyCollectionView().render();
+```javascript
+const bView = myCollectionView.children.findByModel(buttonView.model);
```
-### Automatic Rendering
+#### CollectionView `children`'s: `findByModelCid`
+Find a view by model cid.
-After the initial render the collection view binds to the `update` and
-`reset` events of the collection that is specified.
+```javascript
+const bView = myCollectionView.children.findByModelCid(buttonView.model.cid);
+```
-When the collection for the view is "reset", the view will call `render` on
-itself and re-render the entire collection.
+#### CollectionView `children`'s: `findByIndex`
-When a model is added to the collection, the collection view will render that
-one model into the children.
+Find by numeric index (unstable)
-When a model is removed from a collection (or destroyed / deleted), the collection
-view will destroy and remove that model's child view.
+```javascript
+const bView = myCollectionView.children.findByIndex(0);
+```
-When the collection for the view is sorted, the view will automatically re-sort its child views unless the `sortWithCollection` attribute on the CollectionView is set to `false`.
+#### CollectionView `children`'s: `findIndexByView`
+
+Find the index of the view inside the children
```javascript
-var Bb = require('backbone');
-var Mn = require('backbone.marionette');
+const index = myCollectionView.children.findIndexByView(bView);
+```
-var collection = new Bb.Collection();
+### CollectionView `children` Iterators And Collection Functions
+
+The container object borrows several functions from
+[Underscore.js](http://underscorejs.org/), to provide iterators and other
+collection functions, including:
+
+* [each](http://underscorejs.org/#each)
+* [map](http://underscorejs.org/#map)
+* [reduce](http://underscorejs.org/#reduce)
+* [find](http://underscorejs.org/#find)
+* [filter](http://underscorejs.org/#filter)
+* [reject](http://underscorejs.org/#reject)
+* [every](http://underscorejs.org/#every)
+* [some](http://underscorejs.org/#some)
+* [contains](http://underscorejs.org/#contains)
+* [invoke](http://underscorejs.org/#invoke)
+* [toArray](http://underscorejs.org/#toArray)
+* [first](http://underscorejs.org/#first)
+* [initial](http://underscorejs.org/#initial)
+* [rest](http://underscorejs.org/#rest)
+* [last](http://underscorejs.org/#last)
+* [without](http://underscorejs.org/#without)
+* [isEmpty](http://underscorejs.org/#isEmpty)
+* [pluck](http://underscorejs.org/#pluck)
+* [partition](http://underscorejs.org/#partition)
+
+These methods can be called directly on the container, to iterate and process
+the views held by the container.
+
+```javascript
+import Backbone from 'backbone';
+import { CollectionView } from 'backbone.marionette';
-var MyChildView = Mn.View.extend({
- template: _.noop
+const collectionView = new CollectionView({
+ collection: new Backbone.Collection()
});
-var MyCollectionView = Mn.CollectionView.extend({
- childView: MyChildView,
- collection: collection,
+collectionView.render();
+
+// iterate over all of the views and process them
+collectionView.children.each(function(childView) {
+ // process the `childView` here
});
+```
-var myCollectionView = new MyCollectionView();
+## Listening to Events on the `children`
-// Collection view will not re-render as it has not been rendered
-collection.reset([{foo: 'foo'}]);
+The `CollectionView` can take action depending on what
+events are triggered in its `children`.
+
+Read More:
+- [Child Event Bubbling](./events.md#event-bubbling)
+
+## Self Managed `children`
+
+In addition to children added by Marionette matching the model of a `collection`,
+the `children` of the `CollectionView` can be manually managed.
+
+### Adding a Child View
+
+The `addChildView` method can be used to add a view that is independent of your
+`Backbone.Collection`. This method takes two parameters, the child view instance
+and optionally the index for where it should be placed within the
+[CollectionView's `children`](#managing-children). It returns the added view.
+
+```javascript
+import { CollectionView } from 'backbone.marionette';
+import ButtonView from './button-view';
+
+const MyCollectionView = CollectionView.extend({
+ onRender() {
+ View = new ButtonView();
+ this.addChildView(buttonView, this.children.length);
+ }
+});
+
+const myCollectionView = new MyCollectionView();
myCollectionView.render();
+```
+**Note** Unless an index is specified, this added view will be subject to filtering
+and sorting and may be difficult to manage in complex situations. Use with care.
-// Collection view will re-render displaying the new model
-collection.reset([{foo: 'bar'}]);
+### Removing a Child View
+
+The `removeChildView` method is useful if you need to remove and destroy a view from
+the `CollectionView` without affecting the view's collection. In most cases it is
+better to use the data to determine what the `CollectionView` should display.
+
+This method accepts the child view instance to remove as its parameter. It returns
+the removed view;
+
+```javascript
+import { CollectionView } from 'backbone.marionette';
+
+const MyCollectionView = CollectionView.extend({
+ onChildViewFooEvent(childView, model) {
+ // NOTE: we must wait for the server to confirm
+ // the destroy PRIOR to removing it from the collection
+ model.destroy({wait: true});
+
+ // but go ahead and remove it visually
+ this.removeChildView(childView);
+ }
+});
```
-### Re-render the CollectionView
+### Detaching a Child View
-If you need to re-render the entire collection, you can call the
-`view.render` method. This method takes care of destroying all of
-the child views that may have previously been opened.
+This method is the same as [`removeChildView`](#removing-a-child-view)
+with the exception that the removed view is not destroyed.
-### CollectionView's `attachHtml`
+### Swapping Child Views
-By default the `CollectionView` will append the HTML of each ChildView
-into the element buffer, and then calls the DOM API's [appendContents](./dom.api.md#appendcontentsel-contents) once at the
-end to move the HTML into the collection view's `el`.
+Swap the location of two views in the `CollectionView` `children` and in the `el`.
+This can be useful when sorting is arbitrary or is not performant.
-You can override this by specifying an `attachHtml` method in your
-view definition. This method takes one parameter and has no return
-value.
+**Errors** If either of the two views aren't part of the `CollectionView` an error will be thrown.
+
+If one child is in the `el` but the other is not, [filter](#filtering-the-children) will be called.
```javascript
-var Mn = require('backbone.marionette');
+import Backbone from 'backbone';
+import { CollectionView } from 'backbone.marionette';
+import MyChildView from './my-child-view';
-Mn.CollectionView.extend({
+const collection = new Backbone.Collection([
+ { name: 'first' },
+ { name: 'middle' },
+ { name: 'last' }
+]);
- // The default implementation:
- attachHtml: function(els){
- this.Dom.appendContents(this.el, els);
- }
+const myColView = new CollectionView({
+ collection: collection,
+ childView: MyChildView
+});
+
+myColView.swapChildViews(myColView.children.first(), myColView.children.last());
+
+myColView.children.first().model.get('name'); // "last"
+myColView.children.last().model.get('name'); // "first"
+```
+
+## Sorting the `children`
+
+The `sort` method will loop through the `CollectionView` `children` prior to filtering
+and sort them with the [`viewComparator`](#defining-the-viewcomparator).
+By default, if a `viewComparator` is not set, the `CollectionView` will sort
+the views by the order of the models in the `collection`. If set to `false` view
+sorting will be disabled.
+
+This method is called internally when rendering and
+[`sort` and `before:sort` events](./events.class.md#sort-and-beforesort-events)
+will trigger.
+
+By default the `CollectionView` will maintain a sorted collection's order
+in the DOM. This behavior can be disabled by specifying `{sortWithCollection: false}`
+on initialize.
+### Defining the `viewComparator`
+
+`CollectionView` allows for a custom `viewComparator` option if you want your
+`CollectionView`'s children to be rendered with a different sort order than the
+underlying Backbone collection uses.
+
+```javascript
+import { CollectionView } from 'backbone.marionette';
+
+const myCollectionView = new CollectionView({
+ collection: someCollection,
+ viewComparator: 'otherFieldToSortOn'
});
```
-The first parameter is the instance of the CollectionView that
-will receive the HTML from the second parameter, the HTML buffer.
+```javascript
+import Backbone from 'backbone';
+import { CollectionView } from 'backbone.marionette';
-## CollectionView's `destroy`
+const myCollection = new Backbone.Collection([
+ { id: 1 },
+ { id: 4 },
+ { id: 3 },
+ { id: 2 }
+]);
-`CollectionView` implements a `destroy` method which automatically
-destroys its children and cleans up listeners.
+myCollection.comparator = 'id';
+
+const mySortedColView = new CollectionView({
+ //...
+ collection: myCollection
+});
+
+const myUnsortedColView = new CollectionView({
+ //...
+ collection: myCollection,
+ viewComparator: false
+});
+
+mySortedColView.render(); // 1 4 3 2
+myUnsortedColView.render(); // 1 4 3 2
+
+myCollection.sort();
+// mySortedColView auto-renders 1 2 3 4
+// myUnsortedColView has no change
+```
+
+The `viewComparator` can take any of the acceptable `Backbone.Collection`
+[comparator formats](http://backbonejs.org/#Collection-comparator) -- a sortBy
+(pass a function that takes a single argument), as a sort (pass a comparator
+function that expects two arguments), or as a string indicating the attribute to
+sort by.
+
+#### `getComparator`
+
+Override this method to determine which `viewComparator` to use.
```javascript
-var Bb = require('backbone');
-var Mn = require('backbone.marionette');
+import { CollectionView } from 'backbone.marionette';
+
+const MyCollectionView = CollectionView.extend({
+ sortAsc(model) {
+ return -model.get('order');
+ },
+ sortDesc(model) {
+ return model.get('order');
+ },
+ getComparator() {
+ // The collectionView's model
+ if (this.model.get('sorted') === 'ASC') {
+ return this.sortAsc;
+ }
-var MyChildView = Mn.View.extend({
- template: _.template('ChildView'),
- onDestroy: function() {
- console.log('I will get destroyed');
+ return this.sortDesc;
}
-})
+});
+```
-var myCollectionView = new Mn.CollectionView({
- childView: MyChildView,
- collection: new Bb.Collection([{ id: 1 }])
+#### `setComparator`
+
+The `setComparator` method modifies the `CollectionView`'s `viewComparator`
+attribute and re-sorts. Passing `{ preventRender: true }` in the options argument
+will prevent the view being rendered.
+
+```javascript
+import { CollectionView } from 'backbone.marionette';
+
+const cv = new CollectionView({
+ collection: someCollection
});
-myCollectionView.render();
+cv.render();
-myCollectionView.destroy(); // logs "I will get destroyed"
+// Note: the setComparator is preventing the automatic re-render
+cv.setComparator('orderBy', { preventRender: true });
+
+// Render the children ordered by the orderBy attribute
+cv.render();
```
-## Events
+#### `removeComparator`
+
+This function is actually an alias of `setComparator(null, options)`. It is useful
+for removing the comparator. `removeComparator` also accepts `preventRender` as a option.
+
+```javascript
+import { CollectionView } from 'backbone.marionette';
+
+const cv = new CollectionView({
+ collection: someCollection
+});
+
+cv.render();
+
+cv.setComparator('orderBy');
-The `CollectionView`, like `View`, is able to trigger and respond to events
-occurring during their lifecycle. The [Documentation for Events](./events.md)
-has the complete documentation for how to set and handle events on views.
+//Remove the current comparator without rendering again.
+cv.removeComparator({ preventRender: true });
+```
-### Child Event Bubbling
+### Maintaining the `collection`'s sort
-The collection view is able to monitor and act on events on any children they
-own using [`childViewEvents`](./events.md#explicit-event-listeners) and [`childViewTriggers`](./events.md#triggering-events-on-child-events). Additionally when a child
-view triggers an event, that [event will bubble up](./events.md#event-bubbling) one level to the parent
-collection view. For an example:
+By default the `CollectionView` will maintain a sorted collection's order
+in the DOM. This behavior can be disabled by specifying `{sortWithCollection: false}`
+on initialize or on the view definiton.
```javascript
-var Mn = require('backbone.marionette');
+import Backbone from 'backbone';
+import { CollectionView } from 'backbone.marionette';
+
+const myCollection = new Backbone.Collection([
+ { id: 1 },
+ { id: 4 },
+ { id: 3 },
+ { id: 2 }
+]);
+
+myCollection.comparator = 'id';
+
+const mySortedColView = new CollectionView({
+ //...
+ collection: myCollection
+});
+
+const myUnsortedColView = new CollectionView({
+ //...
+ collection: myCollection,
+ sortWithCollection: false
+});
+
+mySortedColView.render(); // 1 4 3 2
+myUnsortedColView.render(); // 1 4 3 2
+
+myCollection.sort();
+// mySortedColView auto-renders 1 2 3 4
+// myUnsortedColView has no change
+```
+
+## Filtering the `children`
+
+The `filter` method will loop through the `CollectionView`'s sorted `children`
+and test them against the [`viewFilter`](#defining-the-viewfilter).
+The views that pass the `viewFilter`are rendered if necessary and attached
+to the CollectionView and the views that are filtered out will be detached.
+After filtering the `children` will only contain the views to be attached.
+
+If a `viewFilter` exists the
+[`filter` and `before:filter` events](./events.class.md#filter-and-beforefilter-events)
+will trigger.
+
+By default the CollectionView will refilter when views change or when the
+CollectionView is sorted.
+
+**Note** This is a presentation functionality used to easily filter in and out
+constructed children. All children of a `collection` will be instantiated once
+regardless of their filtered status. If you would prefer to manage child view
+instantiation, you should filter the `collection` itself.
+
+### Defining the `viewFilter`
+
+`CollectionView` allows for a custom `viewFilter` option if you want to prevent
+some of the underlying `children` from being attached to the DOM.
+A `viewFilter` can be a function, predicate object. or string.
-var Item = Mn.View.extend({
- tagName: 'li',
+**Errors** An error will be thrown if the `ViewFilter` is not one of these options.
- triggers: {
- 'click a': 'select:item'
+#### `viewFilter` as a function
+
+The `viewFilter` function takes a view from the `children` and returns a truthy
+value if the child should be attached, and a falsey value if it should not.
+
+```javascript
+import Backbone from 'backbone';
+import { CollectionView } from 'backbone.marionette';
+
+const cv = new CollectionView({
+ childView: SomeChildView,
+ emptyView: SomeEmptyView,
+ collection: new Bb.Collection([
+ { value: 1 },
+ { value: 2 },
+ { value: 3 },
+ { value: 4 }
+ ]),
+
+ // Only show views with even values
+ viewFilter(view, index, children) {
+ return view.model.get('value') % 2 === 0;
}
});
-var Collection = Mn.CollectionView.extend({
- tagName: 'ul',
+// renders the views with values '2' and '4'
+cv.render();
+```
+
+#### `viewFilter` as a predicate object
+
+The `viewFilter` predicate object will filter against the view's model attributes.
+
+```javascript
+import Backbone from 'backbone';
+import { CollectionView } from 'backbone.marionette';
+
+const cv = new CollectionView({
+ childView: SomeChildView,
+ emptyView: SomeEmptyView,
+ collection: new Bb.Collection([
+ { value: 1 },
+ { value: 2 },
+ { value: 3 },
+ { value: 4 }
+ ]),
+
+ // Only show views with value 2
+ viewFilter: { value: 2 }
+});
- onChildviewSelectItem: function(childView) {
- console.log('item selected: ' + childView.model.id);
+// renders the view with values '2'
+cv.render();
+```
+
+#### `viewFilter` as a string
+
+The `viewFilter` string represents the view's model attribute and will filter
+truthy values.
+
+```javascript
+import Backbone from 'backbone';
+import { CollectionView } from 'backbone.marionette';
+
+const cv = new CollectionView({
+ childView: SomeChildView,
+ emptyView: SomeEmptyView,
+ collection: new Bb.Collection([
+ { value: 0 },
+ { value: 1 },
+ { value: 2 },
+ { value: null },
+ { value: 4 }
+ ]),
+
+ // Only show views 1,2, and 4
+ viewFilter: 'value'
+});
+
+// renders the view with values '1', '2', and '4'
+cv.render();
+```
+
+#### `getFilter`
+
+Override this function to programatically decide which
+`viewFilter` to use when `filter` is called.
+
+```javascript
+import { CollectionView } from 'backbone.marionette';
+
+const MyCollectionView = CollectionView.extend({
+ summaryFilter(view) {
+ return view.model.get('type') === 'summary';
+ },
+ getFilter() {
+ if (this.collection.length > 100) {
+ return this.summaryFilter;
+ }
+ return this.viewFilter;
}
});
```
-The event will receive a [`childview:` prefix](./events.md#a-child-views-event-prefix) before going through the magic
-method binding logic. See the
-[documentation for Child View Events](./events.md#child-view-events) for more
-information.
+#### `setFilter`
+
+The `setFilter` method modifies the `CollectionView`'s `viewFilter` attribute and filters.
+Passing `{ preventRender: true }` in the options argument will prevent the view
+being rendered.
+
+```javascript
+import { CollectionView } from 'backbone.marionette';
+
+const cv = new CollectionView({
+ collection: someCollection
+});
+
+cv.render();
-### Lifecycle Events
+const newFilter = function(view, index, children) {
+ return view.model.get('value') % 2 === 0;
+};
-The `CollectionView` contains its own lifecycle events, on top of the regular
-`View` event lifecycle. For more information on what these are, and how to use
-them, see the
-[Documentation on `CollectionView` lifecycle events](./viewlifecycle.md#collectionview-lifecycle)
+// Note: the setFilter is preventing the automatic re-render
+cv.setFilter(newFilter, { preventRender: true });
-## Advanced CollectionView Usage
+//Render the new state of the ChildViews instead of the whole DOM.
+cv.render();
+```
-For getting advanced information about filtering, sorting or managing `CollectionView` look at
-[Advanced CollectionView usage](./marionette.collectionviewadvanced.md)
+#### `removeFilter`
-### Managing Children
+This function is actually an alias of `setFilter(null, options)`. It is useful
+for removing filters. `removeFilter` also accepts `preventRender` as a option.
-The `CollectionView` can store and manage its child views. This allows you to easily access
-the views within the collection view, iterate them, find them by a given indexer such as the
-view's model or collection, and more. [Additional Information...](./marionette.collectionviewadvanced.md#collectionviews-children)
+```javascript
+import { CollectionView } from 'backbone.marionette';
-### Filtering
+const cv = new CollectionView({
+ collection: someCollection
+});
-`CollectionView` allows for a custom `viewFilter` option if you want to prevent some of the
-child views from being rendered inside the CollectionView. [Additional Information...](./marionette.collectionviewadvanced.md#collectionviews-filter)
+cv.render();
-### Sorting
+cv.setFilter(function(view, index, children) {
+ return view.model.get('value') % 2 === 0;
+});
-By default the `CollectionView` will maintain a sorted collection's order in the DOM.
-[Additional Information...](./marionette.collectionviewadvanced.md#collectionviews-sort)
+//Remove the current filter without rendering again.
+cv.removeFilter({ preventRender: true });
+```
diff --git a/docs/marionette.collectionviewadvanced.md b/docs/marionette.collectionviewadvanced.md
deleted file mode 100644
index 3e6f033150..0000000000
--- a/docs/marionette.collectionviewadvanced.md
+++ /dev/null
@@ -1,625 +0,0 @@
-# Advanced CollectionView Usage
-
-`CollectionView` provides a lot of possibilities to sort, filter and manages children.
-
-
-## Documentation Index
-
-* [CollectionView's children](#collectionviews-children)
- * [CollectionView's `buildChildView`](#collectionviews-buildchildview)
- * [CollectionView's `addChildView`](#collectionviews-addchildview)
- * [CollectionView: Retrieve Child Views](#collectionview-retrieve-child-views)
- * [CollectionView children's: `findByCid`](#collectionview-children-findbycid)
- * [CollectionView children's: `findByModel`](#collectionview-children-findbymodel)
- * [CollectionView children's: `findByModelCid`](#collectionview-children-findbymodelcid)
- * [CollectionView children's: `findByIndex`](#collectionview-children-findbyindex)
- * [CollectionView children's: `findIndexByView`](#collectionview-children-findindexbyview)
- * [CollectionView's `removeChildView`](#collectionviews-removechildview)
- * [CollectionView's `detachChildView`](#collectionviews-detachchildview)
- * [CollectionView's `swapChildViews`](#collectionviews-swapchildviews)
- * [CollectionView childView Iterators And Collection Functions](#collectionview-childview-iterators-and-collection-functions)
-
-* [CollectionView's `filter`](#collectionviews-filter)
- * [CollectionView's `viewFilter`](#collectionviews-viewfilter)
- * [CollectionView's `getFilter`](#collectionviews-getfilter)
- * [CollectionView's `setFilter`](#collectionviews-setfilter)
- * [CollectionView's `removeFilter`](#collectionviews-removefilter)
-
-* [CollectionView's `sort`](#collectionviews-sort)
- * [CollectionView's `viewComparator`](#collectionviews-viewcomparator)
- * [CollectionView's `getComparator`](#collectionviews-getcomparator)
- * [CollectionView's `setComparator`](#collectionviews-setcomparator)
- * [CollectionView's `removeComparator`](#collectionviews-removecomparator)
- * [CollectionView's `sortWithCollection`](#collectionviews-sortwithcollection)
-
-* [Binding `ui`](#binding-ui)
-
-
-## CollectionView's children
-
-The `CollectionView` can store and manage its child views. This allows you to easily access
-the views within the collection view, iterate them, find them by
-a given indexer such as the view's model or collection, and more.
-
-### CollectionView's `buildChildView`
-
-The `buildChildView` is responsible for taking the ChildView class and
-instantiating it with the appropriate data. This method takes three
-parameters and returns a view instance to be used as thechild view.
-
-```javascript
-buildChildView: function(child, ChildViewClass, childViewOptions){
- // build the final list of options for the childView class
- var options = _.extend({model: child}, childViewOptions);
- // create the child view instance
- var view = new ChildViewClass(options);
- // return it
- return view;
-},
-```
-Override this method when you need a more complicated build, but use [`childView`](./marionette.collectionview.md#collectionviews-childview)
-if you need to determine _which_ View class to instantiate.
-
-```javascript
-var Bb = require('backbone');
-var Mn = require('backbone.marionette');
-
-var MyCollectionView = Mn.CollectionView.extend({
- childView: function(child) {
- if (child.get('type') === 'list') {
- return MyListView;
- }
-
- return MyView;
- },
- buildChildView: function(child, ChildViewClass, childViewOptions) {
- var options = {};
-
- if (child.get('type') === 'list') {
- var childList = new Bb.Collection(child.get('list'));
- options = _.extend({collection: childList}, childViewOptions);
- } else {
- options = _.extend({model: child}, childViewOptions);
- }
-
- // create the child view instance
- var view = new ChildViewClass(options);
- // return it
- return view;
- }
-
-});
-
-```
-
-### CollectionView's `addChildView`
-
-The `addChildView` method can be used to add a view that is independent of your
-`Backbone.Collection`. Note that this added view will be subject to filtering
-and ordering and may be difficult to manage in complex situations. Use with
-care.
-
-This method takes two parameters, the child view instance and the index for
-where it should be placed within the [CollectionView's children](#collectionviews-children). It returns the added view.
-
-```javascript
-var Mn = require('backbone.marionette');
-var buttonView = new ButtonView();
-var MyCollectionView = Mn.CollectionView.extend({
- onRender: function() {
- this.addChildView(buttonView, this.collection.length);
- }
-});
-
-var myCollectionView = new MyCollectionView();
-
-myCollectionView.render();
-```
-
-### CollectionView: Retrieve Child Views
-
-You can retrieve a view by any of the index. If the findBy* method cannot find the view, it will return undefined.
-
-#### CollectionView children's: `findByCid`
-Find a view by it's cid.
-
-```javascript
-var bView = myCollectionView.children.findByCid(buttonView.cid);
-```
-
-#### CollectionView children's: `findByModel`
-Find a view by model.
-
-```javascript
-var bView = myCollectionView.children.findByModel(buttonView.model);
-```
-
-#### CollectionView children's: `findByModelCid`
-Find a view by model cid.
-
-```javascript
-var bView = myCollectionView.children.findByModelCid(buttonView.model.cid);
-```
-
-#### CollectionView children's: `findByIndex`
-
-Find by numeric index (unstable)
-
-```javascript
-var bView = myCollectionView.children.findByIndex(0);
-```
-
-#### CollectionView children's: `findIndexByView`
-
-Find the index of the view inside the children
-
-```javascript
-var index = myCollectionView.children.findIndexByView(bView);
-```
-
-### CollectionView's `removeChildView`
-
-The `removeChildView` method is useful if you need to remove and destroy a view from the `CollectionView` without affecting the view's collection. In most cases it is better to use the data to determine what the `CollectionView` should display.
-
-This method accepts the child view instance to remove as its parameter. It returns the removed view;
-
-```javascript
-var Mn = require('backbone.marionette');
-
-Mn.CollectionView.extend({
- onChildViewClose: function(childView, model) {
- // NOTE: we must wait for the server to confirm
- // the destroy PRIOR to removing it from the collection
- model.destroy({wait: true});
-
- // but go ahead and remove it visually
- this.removeChildView(childView);
- }
-});
-```
-
-### CollectionView's `detachChildView`
-
-This method is the same as [`removeChildView`](#collectionviews-removechildview)
-with the exception that the removed view is not destroyed.
-
-### CollectionView's `swapChildViews`
-
-Swap the location of two views in the `CollectionView` `children` and in the `el`.
-This can be useful when sorting is arbitrary or is not performant.
-
-If either of the two views aren't part of the `CollectionView` an error will be thrown.
-
-If one child is in the `el` but the other is not, [filter](#collectionviews-filter) will be called.
-
-```javascript
-var Mn = require('backbone.marionette');
-
-var collection = new Backbone.Collection([
- { name: 'first' },
- { name: 'middle' },
- { name: 'last' }
-]);
-
-var myCollection = new Mn.CollectionView({
- collection: collection,
- childView: MyChildView
-});
-
-myCollection.swapChildViews(myCollection.children.first(), myCollection.children.last());
-
-myCollection.children.first().model.get('name'); // "last"
-myCollection.children.last().model.get('name'); // "first"
-```
-
-### CollectionView childView Iterators And Collection Functions
-
-The container object borrows several functions from
-[Underscore.js](http://underscorejs.org/), to provide iterators and other
-collection functions, including:
-
-* [each](http://underscorejs.org/#each)
-* [map](http://underscorejs.org/#map)
-* [reduce](http://underscorejs.org/#reduce)
-* [find](http://underscorejs.org/#find)
-* [filter](http://underscorejs.org/#filter)
-* [reject](http://underscorejs.org/#reject)
-* [every](http://underscorejs.org/#every)
-* [some](http://underscorejs.org/#some)
-* [contains](http://underscorejs.org/#contains)
-* [invoke](http://underscorejs.org/#invoke)
-* [toArray](http://underscorejs.org/#toArray)
-* [first](http://underscorejs.org/#first)
-* [initial](http://underscorejs.org/#initial)
-* [rest](http://underscorejs.org/#rest)
-* [last](http://underscorejs.org/#last)
-* [without](http://underscorejs.org/#without)
-* [isEmpty](http://underscorejs.org/#isEmpty)
-* [pluck](http://underscorejs.org/#pluck)
-* [partition](http://underscorejs.org/#partition)
-
-These methods can be called directly on the container, to iterate and process
-the views held by the container.
-
-```javascript
-var Bb = require('backbone');
-var Mn = require('backbone.marionette');
-
-var collectionView = new Mn.CollectionView({
- collection: new Bb.Collection()
-});
-
-collectionView.render();
-
-// iterate over all of the views and process them
-collectionView.children.each(function(childView) {
- // process the `childView` here
-});
-```
-
-## CollectionView's `filter`
-
-The `filter` method will loop through the `CollectionView` `children`
-and test them against the [`viewFilter`](#collectionviews-viewfilter).
-The views that pass the `viewFilter`are rendered if necessary and attached
-to the CollectionView and the views that are filtered out will be detached.
-If a `viewFilter` exists the `before:filter` and `filter` events will be triggered.
-By default the CollectionView will refilter when views change or when the
-CollectionView is sorted.
-
-### CollectionView's `viewFilter`
-
-`CollectionView` allows for a custom `viewFilter` option if you want to prevent
-some of the underlying `children` from being attached to the DOM.
-A `viewFilter` can be a function, predicate object. or string.
-
-#### CollectionView's `viewFilter` as a function
-
-The `viewFilter` function takes a view from the `children` and returns a truthy
-value if the child should be attached, and a falsey value if it should not.
-
-```javascript
-var Bb = require('backbone');
-var Mn = require('backbone.marionette');
-
-var cv = new Mn.CollectionView({
- childView: SomeChildView,
- emptyView: SomeEmptyView,
- collection: new Bb.Collection([
- { value: 1 },
- { value: 2 },
- { value: 3 },
- { value: 4 }
- ]),
-
- // Only show views with even values
- viewFilter: function (view, index, children) {
- return view.model.get('value') % 2 === 0;
- }
-});
-
-// renders the views with values '2' and '4'
-cv.render();
-```
-
-#### CollectionView's `viewFilter` as a predicate object
-
-The `viewFilter` predicate object will filter against the view's model attributes.
-
-```javascript
-var Bb = require('backbone');
-var Mn = require('backbone.marionette');
-
-var cv = new Mn.CollectionView({
- childView: SomeChildView,
- emptyView: SomeEmptyView,
- collection: new Bb.Collection([
- { value: 1 },
- { value: 2 },
- { value: 3 },
- { value: 4 }
- ]),
-
- // Only show views with even values
- viewFilter: { value: 2 }
-});
-
-// renders the view with values '2'
-cv.render();
-```
-
-#### CollectionView's `viewFilter` as a predicate object
-
-The `viewFilter` string represents the view's model attribute and will filter
-truthy values.
-
-```javascript
-var Bb = require('backbone');
-var Mn = require('backbone.marionette');
-
-var cv = new Mn.CollectionView({
- childView: SomeChildView,
- emptyView: SomeEmptyView,
- collection: new Bb.Collection([
- { value: 0 },
- { value: 1 },
- { value: 2 },
- { value: null },
- { value: 4 }
- ]),
-
- // Only show views 1,2, and 4
- viewFilter: 'value'
-});
-
-// renders the view with values '2'
-cv.render();
-```
-
-### CollectionView's `getFilter`
-
-Override this function to programatically decide which
-`viewFilter` to use when `filter` is called.
-
-```javascript
-var Mn = require('backbone.marionette');
-
-var MyCollectionView = Mn.CollectionView.extend({
- summaryFilter: function(view) {
- return view.model.get('type') === 'summary';
- },
- getFilter: function() {
- if(this.collection.length > 100) {
- return this.summaryFilter;
- }
- return this.viewFilter;
- }
-});
-```
-
-### CollectionView's `setFilter`
-
-The `setFilter` method modifies the `CollectionView`'s `viewFilter` attribute and filters.
-Passing `{ preventRender: true }` in the options argument will prevent the view
-being rendered.
-
-```javascript
-var Mn = require('backbone.marionette');
-
-var cv = new Mn.CollectionView({
- collection: someCollection
-});
-
-cv.render();
-
-var newFilter = function(view, index, children) {
- return view.model.get('value') % 2 === 0;
-};
-
-// Note: the setFilter is preventing the automatic re-render
-cv.setFilter(newFilter, { preventRender: true });
-
-//Render the new state of the ChildViews instead of the whole DOM.
-cv.render();
-```
-
-### CollectionView's `removeFilter`
-
-This function is actually an alias of `setFilter(null, options)`. It is useful
-for removing filters. `removeFilter` also accepts `preventRender` as a option.
-
-```javascript
-var Mn = require('backbone.marionette');
-
-var cv = new Mn.CollectionView({
- collection: someCollection
-});
-
-cv.render();
-
-cv.setFilter(function(view, index, children) {
- return view.model.get('value') % 2 === 0;
-});
-
-//Remove the current filter without rendering again.
-cv.removeFilter({ preventRender: true });
-```
-
-## CollectionView's `sort`
-
-The `sort` method will loop through the `CollectionView` `children`
-and sort them with the [`viewComparator`](#collectionviews-viewcomparator).
-By default, if a `viewComparator` is not set, the `CollectionView` will sort
-the views by the order of the models in the collection. If set to `false` view
-sorting will be disabled.
-This method is also triggered internally when rendering and `before:sort` and
-`sort` events will be triggered before and after sorting.
-
-By default the `CollectionView` will maintain a sorted collection's order
-in the DOM. This behavior can be disabled by specifying `{sortWithCollection: false}`
-on initialize.
-
-### CollectionView's `viewComparator`
-
-`CollectionView` allows for a custom `viewComparator` option if you want your
-`CollectionView`'s children to be rendered with a different sort order than the
-underlying Backbone collection uses.
-
-```javascript
-var Mn = require('backbone.marionette');
-
-var cv = new Mn.CollectionView({
- collection: someCollection,
- viewComparator: 'otherFieldToSortOn'
-});
-```
-
-```javascript
-var Bb = require('backbone');
-var Mn = require('backbone.marionette');
-
-var myCollection = new Bb.Collection([
- { id: 1 },
- { id: 4 },
- { id: 3 },
- { id: 2 }
-]);
-
-myCollection.comparator = 'id';
-
-var mySortedColView = new Mn.CollectionView({
- //...
- collection: myCollection
-});
-
-var myUnsortedColView = new Mn.CollectionView({
- //...
- collection: myCollection,
- sort: false
-});
-
-mySortedColView.render(); // 1 4 3 2
-myUnsortedColView.render(); // 1 4 3 2
-
-// mySortedColView auto-renders 1 2 3 4
-// myUnsortedColView has no change
-myCollection.sort();
-```
-
-The `viewComparator` can take any of the acceptable `Backbone.Collection`
-[comparator formats](http://backbonejs.org/#Collection-comparator) -- a sortBy
-(pass a function that takes a single argument), as a sort (pass a comparator
-function that expects two arguments), or as a string indicating the attribute to
-sort by.
-
-### CollectionView's `getComparator`
-
-Override this method to determine which `viewComparator` to use.
-
-```javascript
-var Mn = require('backbone.marionette');
-
-var MyCollectionView = Mn.CollectionView.extend({
- sortAsc: function(model) {
- return -model.get('order');
- },
- sortDesc: function(model) {
- return model.get('order');
- },
- getComparator: function() {
- // The collectionView's model
- if (this.model.get('sorted') === 'ASC') {
- return this.sortAsc;
- }
-
- return this.sortDesc;
- }
-});
-```
-
-### CollectionView's `setComparator`
-
-The `setComparator` method modifies the `CollectionView`'s `viewComparator`
-attribute and re-sorts. Passing `{ preventRender: true }` in the options argument
-will prevent the view being rendered.
-
-```javascript
-var Mn = require('backbone.marionette');
-
-var cv = new Mn.CollectionView({
- collection: someCollection
-});
-
-cv.render();
-
-// Note: the setComparator is preventing the automatic re-render
-cv.setComparator('orderBy', { preventRender: true });
-
-// Render the children ordered by the orderBy attribute
-cv.render();
-```
-
-### CollectionView's `removeComparator`
-
-This function is actually an alias of `setComparator(null, options)`. It is useful
-for removing the comparator. `removeComparator` also accepts `preventRender` as a option.
-
-```javascript
-var Mn = require('backbone.marionette');
-
-var cv = new Mn.CollectionView({
- collection: someCollection
-});
-
-cv.render();
-
-cv.setComparator('orderBy');
-
-//Remove the current comparator without rendering again.
-cv.removeComparator({ preventRender: true });
-```
-
-### CollectionView's `sortWithCollection`
-
-By default the `CollectionView` will maintain a sorted collection's order
-in the DOM. This behavior can be disabled by specifying `{sortWithCollection: false}` on initialize or on the view definiton
-
-```javascript
-var Bb = require('backbone');
-var Mn = require('backbone.marionette');
-
-var myCollection = new Bb.Collection([
- { id: 1 },
- { id: 4 },
- { id: 3 },
- { id: 2 }
-]);
-
-myCollection.comparator = 'id';
-
-var mySortedColView = new Mn.CollectionView({
- //...
- collection: myCollection
-});
-
-var myUnsortedColView = new Mn.CollectionView({
- //...
- collection: myCollection,
- sortWithCollection: false
-});
-
-mySortedColView.render(); // 1 4 3 2
-myUnsortedColView.render(); // 1 4 3 2
-
-// mySortedColView auto-renders 1 2 3 4
-// myUnsortedColView has no change
-myCollection.sort();
-```
-
-## Binding `ui`
-
-By default, `CollectionView` will not bind the `ui` object. As it has no direct
-`template` of its own to manage, this isn't usually an issue. There may be
-instances where binding `ui` is helpful when you want to access elements inside
-`CollectionView`s children with `getUI()`.
-
-If you need to bind `ui` yourself, you can just run `bindUIElements` on the
-collection:
-
-```javascript
-var Mn = require('backbone.marionette');
-
-var MyCollectionView = Mn.CollectionView.extend({
- ...
-
- ui: {
- checkbox: 'input[type="checkbox"]'
- }
-});
-
-var collectionView = new MyCollectionView();
-
-collectionView.bindUIElements();
-
-console.log(collectionView.getUI('checkbox')); // Output all checkboxes.
-```
diff --git a/docs/marionette.mnobject.md b/docs/marionette.mnobject.md
index 4030e7d735..5cb5d8f6f5 100644
--- a/docs/marionette.mnobject.md
+++ b/docs/marionette.mnobject.md
@@ -2,16 +2,29 @@
`MnObject` incorporates backbone conventions `initialize`, `cid` and `extend`.
`MnObject` includes:
-- [Common Marionette Functionality](./common.md).
-- [Radio API](./backbone.radio.md#marionette-integration).
+- [Common Marionette Functionality](./common.md)
+- [Class Events](./events.class.md#mnobject-events)
+- [Radio API](./backbone.radio.md#marionette-integration)
## Documentation Index
+* [Instantiating a MnObject](#instantiating-a-mnobject)
* [Unique Client ID](#unique-client-id)
-* [Initialize](#initialize)
* [Destroying a MnObject](#destroying-a-mnobject)
* [Basic Use](#basic-use)
+## Instantiating a MnObject
+
+When instantiating a `MnObject` there are several properties, if passed,
+that will be attached directly to the instance:
+`channelName`, `radioEvents`, `radioRequests`
+
+```javascript
+import { MnObject } from 'backbone.marionette';
+
+const myObject = new MnObject({ ... });
+```
+
## Unique Client ID
The `cid` or client id is a unique identifier automatically assigned to MnObjects
when they're first created and by default is prefixed with `mno`.
@@ -29,24 +42,6 @@ const foo = new MyFoo();
console.log(foo.cid); // foo1234
```
-## Initialize
-`initialize` is called immediately after the MnObject has been instantiated,
-and is invoked with the same arguments that the constructor received.
-
-```javascript
-import { MnObject } from 'backbone.marionette';
-
-const Friend = MnObject.extend({
- initialize(options){
- console.log(options.name);
- }
-});
-
-new Friend({name: 'John'});
-```
-
-[Live example](https://jsfiddle.net/marionettejs/1ytrwyog/)
-
## Destroying a MnObject
### `destroy`
diff --git a/docs/marionette.region.md b/docs/marionette.region.md
index a779846c08..8b12687881 100644
--- a/docs/marionette.region.md
+++ b/docs/marionette.region.md
@@ -1,21 +1,26 @@
# Regions
Regions provide consistent methods to manage, show and destroy
-views in your applications and layouts. You can use a jQuery selector to
-identify where your region must be displayed.
+views in your applications and views.
+
+`Region` includes:
+- [Common Marionette Functionality](./common.md)
+- [Class Events](./events.class.md#region-events)
+- [The DOM API](./dom.api.md)
See the documentation for [laying out views](./marionette.view.md#laying-out-views---regions) for an introduction in
managing regions throughout your application.
-Regions maintain the [View's lifecycle](./viewlifecycle.md#regions-and-the-view-lifecycle) while showing or emptying a view.
+Regions maintain the [View's lifecycle](./view.lifecycle.md) while showing or emptying a view.
## Documentation Index
+* [Instantiating a Region](#instantiating-a-region)
* [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)
+ * [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)
@@ -30,10 +35,26 @@ Regions maintain the [View's lifecycle](./viewlifecycle.md#regions-and-the-view-
* [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](#set-how-views-el-is-attached)
* [Configure How To Remove View](#configure-how-to-remove-view)
+## Instantiating a Region
+
+When instantiating a `Region` there are two properties, if passed,
+that will be attached directly to the instance:
+`el`, and `replaceElement`.
+
+```javascript
+import { Region } from 'backbone.marionette';
+
+const myRegion = new Region({ ... });
+```
+
+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.
+
## Defining the Application Region
The Application defines a single region `el` using the `region` attribute. This
@@ -41,37 +62,39 @@ can be accessed through `getRegion()` or have a view displayed directly with
`showView()`. Below is a short example:
```javascript
-var Mn = require('backbone.marionette');
-var SomeView = require('./view');
+import { Application } from 'backbone.marionette';
+import SomeView from './view';
-var App = Mn.Application.extend({
+const MyApp = Application.extend({
region: '#main-content',
- onStart: function() {
- var main = this.getRegion(); // Has all the properties of a `Region`
- main.show(new SomeView());
+ onStart() {
+ const mainRegion = this.getRegion(); // Has all the properties of a `Region`
+ mainRegion.show(new SomeView());
}
});
```
For more information, see the
-[Application docs](./marionette.application.md#root-layout).
+[Application docs](./marionette.application.md#application-region).
## Defining Regions
-Marionette supports multiple ways to define regions on your `Application` or
-`View`. This section will document the different types as applied to `View`,
-although they will work for `Application` as well - just replace `regions` with
-`region` in your definition.
+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.
+
+**Errors** An error will be thrown for an incorrect region configuration.
### String Selector
You can use a jQuery string selector to define regions.
```javascript
-var Mn = require('backbone.marionette');
+import { View } from 'backbone.marionette';
-var MyView = Mn.View.extend({
+const MyView = View.extend({
regions: {
mainRegion: '#main'
}
@@ -89,13 +112,13 @@ To overwrite the parent `el` of the region with the rendered contents of the
inner View, use `replaceElement` as so:
```javascript
-var Mn = require('backbone.marionette');
+import { View } from 'backbone.marionette';
-var OverWriteView = Mn.View.extend({
+const OverWriteView = View.extend({
className: '.new-class'
});
-var MyView = Mn.View.extend({
+const MyView = View.extend({
regions: {
main: {
el: '.overwrite-me',
@@ -103,7 +126,7 @@ var MyView = Mn.View.extend({
}
}
});
-var view = new MyView();
+const view = new MyView();
view.render();
console.log(view.$('.overwrite-me').length); // 1
@@ -122,7 +145,9 @@ these elements are usually very strict on what content they will allow.
```js
-var MyView = Mn.View.extend({
+import { View } from 'backbone.marionette';
+
+const MyView = View.extend({
regions: {
regionDefinition: {
el: '.bar',
@@ -132,16 +157,19 @@ var MyView = Mn.View.extend({
});
```
-### Specifying regions as a Function
+**Errors** An error will be thrown in the regions `el` is not specified,
+or if the `el` does not exist in the html.
-The `regions` attribute on a view can be a
+### Specifying `regions` as a Function
+
+On a `View` the `regions` attribute can also be a
[function returning an object](./basics.md#functions-returning-values):
```javascript
-var Mn = require('backbone.marionette');
+import { View } from 'backbone.marionette';
-var MyView = Mn.View.extend({
- regions: function(){
+const MyView = View.extend({
+ regions(){
return {
firstRegion: '#first-region'
};
@@ -154,24 +182,30 @@ var MyView = Mn.View.extend({
If you've created a custom region class, you can use it to define your region.
```javascript
-var Mn = require('backbone.marionette');
+import { Application, Region, View } from 'backbone.marionette';
-var MyRegion = Mn.Region.extend({
- onShow: function(){
+const MyRegion = Region.extend({
+ onShow(){
// Scroll to the middle
this.$el.scrollTop(this.currentView.$el.height() / 2 - this.$el.height() / 2);
}
});
-var MyView = Mn.View.extend({
+const MyApp = Application.extend({
+ regionClass: MyRegion,
+ region: '#first-region'
+})
+
+const MyView = View.extend({
+ regionClass: MyRegion,
regions: {
firstRegion: {
el: '#first-region',
- regionClass: MyRegion
- }
+ regionClass: Region // Don't scroll this to the top
+ },
+ secondRegion: '#second-region'
}
});
-
```
[Live example](https://jsfiddle.net/marionettejs/oLLrzx8g/)
@@ -182,9 +216,9 @@ The UI attribute can be useful when setting region selectors - simply use
the `@ui.` prefix:
```javascript
-var Mn = require('backbone.marionette');
+import { View } from 'backbone.marionette';
-var MyView = Mn.View.extend({
+const MyView = View.extend({
ui: {
region: '#first-region'
},
@@ -192,7 +226,6 @@ var MyView = Mn.View.extend({
firstRegion: '@ui.region'
}
});
-
```
[Live example](https://jsfiddle.net/marionettejs/ey1od1g8/)
@@ -203,9 +236,9 @@ To add regions to a view after it has been instantiated, simply use the
`addRegion` method:
```javascript
-var MyView = require('./myview');
+import MyView from './myview';
-myView = new MyView();
+const myView = new MyView();
myView.addRegion('thirdRegion', '#third-region');
```
@@ -214,9 +247,9 @@ Now we can access `thirdRegion` as we would the others.
You can also add multiple regions using `addRegions`.
```javascript
-var MyView = require('./myview');
+import MyView from './myview';
-myView = new MyView();
+const myView = new MyView();
myView.addRegions({
main: {
el: '.overwrite-me',
@@ -228,12 +261,13 @@ myView.addRegions({
## 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.
+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
-var Mn = require('backbone.marionette');
+import { View } from 'backbone.marionette';
-var MyView = Mn.View.extend({
+const MyView = View.extend({
regions: {
main: '.main',
sidebar: '.sidebar',
@@ -241,10 +275,10 @@ var MyView = Mn.View.extend({
}
});
-var myView = new MyView();
+const myView = new MyView();
// remove only the main region
-var mainRegion = myView.removeRegion('main');
+const mainRegion = myView.removeRegion('main');
mainRegion.isDestroyed(); // -> true
@@ -255,12 +289,11 @@ myView.removeRegions();
## Using Regions on a view
In addition to adding and removing regions there are a few
-methods to help utilize regions.
+methods to help utilize regions. All of these methods will first
+render an unrendered view so that regions are properly initialized.
- `getRegion(name)` - Request a region from a view by name.
- - Note: If the view hasn't been rendered at this point, it will be.
- `getRegions()` - Returns an object literal of all regions on the view organized by name.
- - Note: If the view hasn't been rendered at this point, it will be.
- `hasRegion(name)` - Check if a view has a region.
- `emptyRegions()` - Empty all of the regions on a view.
@@ -269,29 +302,31 @@ methods to help utilize regions.
Once a region is defined, you can call its `show` method to display the view:
```javascript
-var myView = new MyView();
-var childView = new MyChildView();
-var mainRegion = myView.getRegion('main');
+const myView = new MyView();
+const childView = new MyChildView();
+const mainRegion = myView.getRegion('main');
// render and display the view
-mainRegion.show(childView);
+mainRegion.show(childView, { fooOption: 'bar' });
```
This is equivalent to a view's `showChildView` which can be used as:
```javascript
-var myView = new MyView();
-var childView = new MyChildView();
+const myView = new MyView();
+const childView = new MyChildView();
// render and display the view
-myView.showChildView('main', childView);
+myView.showChildView('main', childView, { fooOption: 'bar' });
```
Both forms take an `options` object that will be passed to the
-[events fired during `show`](./viewlifecycle.md#show-view-events).
+[events fired during `show`](./events.class.md#show-and-beforeshow-events).
For more information on `showChildView` and `getChildView`, see the
-[Documentation for Views](./marionette.view.md#managing-sub-views)
+[Documentation for Views](./marionette.view.md#managing-children)
+
+**Errors** An error will be thrown if the view is falsy or destroyed.
### Checking whether a region is showing a view
@@ -300,8 +335,8 @@ function. This will return a boolean value depending whether or not the region
is showing a view.
```javascript
-var myView = new MyView();
-var mainRegion = myView.getRegion('main');
+const myView = new MyView();
+const mainRegion = myView.getRegion('main');
mainRegion.hasView() // false
mainRegion.show(new OtherView());
@@ -314,78 +349,52 @@ If you show a view in a region with an existing view, Marionette will
### Non-Marionette Views
Marionette Regions aren't just for showing Marionette Views - they can also
-display instances of regular [`Backbone.View`](http://backbonejs.org/#View).
+display instances of a [`Backbone.View`](http://backbonejs.org/#View).
To do this, ensure your view defines a `render()` method and just treat it like
a regular Marionette View:
```javascript
-var Bb = require('backbone');
-var Mn = require('backbone.marionette');
-var _ = require('underscore');
+import _ from 'underscore';
+import Bb from 'backbone';
+import { View } from 'backbone.marionette';
-var MyChildView = Bb.View.extend({
- render: function() {
+const MyChildView = Bb.View.extend({
+ render() {
this.$el.append('Title
'),
regions: {
firstRegion: '#first-region'
},
- onRender: function() {
+ onRender() {
this.showChildView('firstRegion', new SubView());
}
});
@@ -213,29 +194,30 @@ Note: If `view.showChildView(region, subView)` is invoked before the `view` has
[Live example](https://jsfiddle.net/marionettejs/98u073m0/)
-#### Accessing a Child View
+### Accessing a Child View
-To access the child view of a `View` - use the `getChildView(region)` method.
+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, or `null`:
```javascript
-var Mn = require('backbone.marionette');
-var SubView = require('./subview');
+import _ from 'underscore';
+import { View } from 'backbone.marionette'
+import SubView from './subview';
-var MyView = Mn.View.extend({
- template: '#tpl-view-with-regions',
+const MyView = View.extend({
+ _.template('Title
'),
regions: {
firstRegion: '#first-region'
},
- onRender: function() {
+ onRender() {
this.showChildView('firstRegion', new SubView());
},
- onSomeEvent: function() {
- var first = this.getChildView('firstRegion');
+ onSomeEvent() {
+ const first = this.getChildView('firstRegion');
first.doSomething();
}
});
@@ -245,534 +227,91 @@ var MyView = Mn.View.extend({
If no view is available, `getChildView` returns `null`.
-#### Detaching a Child View
+### Detaching a Child View
You can detach a child view from a region through `detachChildView(region)`
```javascript
-
-var Mn = require('backbone.marionette');
-var SubView = require('./subview');
-
-var MyView = Mn.View.extend({
- template: '#tpl-view-with-regions',
-
+import _ from 'underscore';
+import { View } from 'backbone.marionette'
+import SubView from './subview';
+
+const MyView = View.extend({
+ template: _.template(`
+ Title
+
+
+ `),
regions: {
firstRegion: '#first-region',
secondRegion: '#second-region'
},
- onRender: function() {
+ onRender() {
this.showChildView('firstRegion', new SubView());
},
- onMoveView: function() {
- var view = this.detachChildView('firstRegion');
+ onMoveView() {
+ const view = this.detachChildView('firstRegion');
this.showChildView('secondRegion', view);
}
});
```
This is a proxy for [region.detachView()](./marionette.region.md#detaching-existing-views)
+### Destroying a Child View
+
+There are two ways to easily destroy a child view.
+
+```javascript
+// Directly
+myChildView.getChildView('regionName').destroy();
+
+// Indirectly
+myChildView.getRegion('regionName').empty();
+```
+
### Region Availability
Any defined regions within a `View` will be available to the `View` or any
-calling code immediately after instantiating the `View`. This allows a View to
-be attached to an existing DOM element in an HTML page, without the need to call
-a render method or anything else, to create the regions.
+calling code immediately after rendering the `View`. Using `getRegion` or any
+of the child view methods above will first render the view so that the region is
+available.
-However, a region will only be able to populate itself if the `View` has access
-to the elements specified within the region definitions. That is, if your view
-has not yet rendered, your regions may not be able to find the element that
-you've specified for them to manage. In that scenario, using the region will
-result in no changes to the DOM.
-
-### Efficient Nested View Structures
+## Efficient Nested View Structures
When your views get some more regions, you may want to think of the most
efficient way to render your views. Since manipulating the DOM is performance
heavy, it's best practice to render most of your views at once.
Marionette provides a simple mechanism to infinitely nest views in a single
-paint: just render all of the children in the onRender callback.
+paint: just render all of the children in the `onRender` callback for the
+[`render` event](./events.class.md#render-and-beforerender-events).
```javascript
-var Mn = require('backbone.marionette');
+import { View } from 'backbone.marionette';
-var ParentView = Mn.View.extend({
- onRender: function() {
+const ParentView = View.extend({
+ // ...
+ onRender() {
this.showChildView('header', new HeaderView());
this.showChildView('footer', new FooterView());
}
});
-myRegion.show(new ParentView(), options);
+myRegion.show(new ParentView());
```
-In this example, the doubly-nested view structure will be rendered in a single
-paint.
+In this example, the doubly-nested view structure will be rendered in a single paint.
This system is recursive, so it works for any deeply nested structure. The child
views you show can render their own child views within their onRender callbacks!
-### Listening to Events on Children
+## 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
-is happening in views it directly owns.
-
-**To see more information about events, see the [events documentation](./events.md#child-view-events)**
-
-## Organizing Your View
-
-The `View` provides a mechanism to name parts of your template to be used
-throughout the view with the `ui` attribute. This provides a number of benefits:
-
-1. Provide a reference to commonly used UI elements
-2. Cache the jQuery selector
-3. Change the selector later in only one place in your view
-
-### Defining `ui`
-
-To define your `ui` hash, just set an object of key to jQuery selectors to the
-`ui` attribute of your View:
-
-```javascript
-var Mn = require('backbone.marionette');
+events are triggered in views it directly owns.
-var MyView = Mn.View.extend({
- template: '#my-template',
- ui: {
- save: '#save-button',
- close: '.close-button'
- }
-});
-```
-
-Inside your view, the `save` and `close` references will point to the jQuery
-selectors `#save-button` and `.close-button` respectively.
-
-### Accessing UI Elements
-
-To get the handles to your UI elements, use the `getUI(ui)` method:
-
-```javascript
-var Mn = require('backbone.marionette');
-
-var MyView = Mn.View.extend({
- template: '#my-template',
- ui: {
- save: '#save-button',
- close: '.close-button'
- },
-
- onDoSomething: function() {
- var saveButton = this.getUI('save');
- saveButton.addClass('disabled');
- saveButton.attr('disabled', 'disabled');
- }
-});
-```
-
-[Live example](https://jsfiddle.net/marionettejs/rpa58v0g/)
-
-As `saveButton` here is a jQuery selector, you can call any jQuery methods on
-it, according to the jQuery documentation.
-
-#### Referencing UI in `events` and `triggers`
-
-The UI attribute is especially useful when setting handlers in the
-[`events`](#view-events) and [`triggers`](#view-triggers) objects - simply use
-the `@ui.` prefix:
-
-```javascript
-var Mn = require('backbone.marionette');
-
-var MyView = Mn.View.extend({
- template: '#my-template',
- ui: {
- save: '#save-button',
- close: '.close-button'
- },
-
- events: {
- 'click @ui.save': 'handleSave'
- },
-
- triggers: {
- 'click @ui.close': 'close:view'
- },
-
- handleSave: function() {
- this.model.save();
- }
-});
-```
-
-[Live example](https://jsfiddle.net/marionettejs/f2k0wu05/)
-
-In this example, when the user clicks on `#save-button`, `handleSave` will be
-called. If the user clicks on `.close-button`, then the event `close:view` will
-be fired on `MyView`.
-
-By prefixing with `@ui`, we can change the underlying template without having to
-hunt through our view for every place where that selector is referenced - just
-update the `ui` object.
-
-## Events
-
-Firing events on views allows you to communicate that something has happened
-on that view and allowing it to decide whether to act on it or not.
-
-During the create/destroy lifecycle for a `View`, Marionette will call a number
-of events on the view being created and attached. You can listen to these events
-and act on them in two ways:
-
- 1. The typical Backbone manner: `view.on('render', function() {})`
- 2. Overriding the onEvent listener methods: `onRender: function() {}`
-
-### onEvent Listeners
-
-Marionette creates onEvent listeners for all events fired using
-`view.triggerMethod('event')` - if there is an `onEvent` method, Marionette will
-call it for you. An example:
-
-```javascript
-var Mn = require('backbone.marionette');
-
-var MyView = Mn.View.extend({
- onRender: function() {
- console.log("Fired whenever view.triggerMethod('render') is called.");
- },
-
- onOtherEvent: function(argument) {
- console.log("Fired other:event with '" + argument + "' as an argument");
- }
-});
-
-var view = new MyView();
-
-view.triggerMethod('other:event', 'test argument');
-```
-
-[Live example](https://jsfiddle.net/marionettejs/wb95xd3m/)
-
-This will display in the console:
-`Fired other:event with 'test argument' as an argument`
-
-To set up handlers for events, see the rules in the
-[Documentation for Events](./events.md#magic-method-binding).
-
-### Lifecycle Events
-
-When rendering and showing a `View`, a number of events will be fired to denote
-certain stages of the creation, or destruction, lifecycle have been reached.
-For a full list of events, and how to use them, see the
-[documentation for `View` lifecycle events](./viewlifecycle.md#view-lifecycle).
-
-### Binding To User Input
-
-Views can bind custom events whenever users perform some interaction with the
-DOM. Using the view `events` and `triggers` handlers lets us either bind user
-input directly to an action or fire a generic trigger that may or may not be
-handled.
-
-#### Event and Trigger Mapping
-
-The `events` and `triggers` attributes bind DOM events to actions to perform on
-the view. They each take a DOM event key and a mapping to the handler.
-
-We'll cover a simple example:
-
-```javascript
-var Mn = require('backbone.marionette');
-
-var MyView = Mn.View.extend({
- events: {
- 'drop': 'coversEntireElement',
- 'click a': 'showModal',
- 'click @ui.save': 'saveForm'
- },
-
- triggers: {
- 'click @ui.close': 'cancel:entry'
- },
-
- ui: {
- save: '.btn-save',
- close: '.btn-cancel'
- },
-
- showModal: function() {
- console.log('Show the modal');
- },
-
- saveForm: function() {
- console.log('Save the form');
- }
- coversEntireElement: function() {
- console.log('Handle a drop event anywhere in the element');
- }
-});
-```
-
-Event listeners are constructed by:
-
-```javascript
-'` tag.
-
-[Live example](https://jsfiddle.net/marionettejs/h762zjua/)
-
-## Documentation Index
-
-* [Rendering a Template](#rendering-a-template)
- * [jQuery Selector](#jquery-selector)
- * [Template Function](#template-function)
- * [The `getTemplate` function](#the-gettemplate-function)
-* [Models and Collections](#models-and-collections)
- * [Rendering a Model](#rendering-a-model)
- * [Rendering a Collection](#rendering-a-collection)
- * [User Interaction with Collections](#user-interaction-with-collections)
- * [Model/Collection Rendering Rules](#modelcollection-rendering-rules)
-* [Template Context](#template-context)
- * [Context Object](#context-object)
- * [Binding of `this`](#binding-of-this)
-* [Serializing Model and Collection Data](#serializing-model-and-collection-data)
-
-## Rendering a Template
-
-### jQuery Selector
-
-If your index page contains a template element formatted for Underscore, you can
-simply pass in the jQuery selector for it to `template` and Marionette will look
-it up:
-
-```javascript
-var Mn = require('backbone.marionette');
-
-export.MyView = Mn.View.extend({
- template: '#template-layout'
-});
-```
-
-```html
-
-```
-
-[Live example](https://jsfiddle.net/marionettejs/z4sd7ssh/)
-
-Marionette compiles the template above using `_.template` and renders it for
-you when `MyView` gets rendered.
-
-### Template Function
-
-A more common way of setting a template is to assign a function to `template`
-that renders its argument. This will commonly be the `_.template` function:
-
-```javascript
-var _ = require('underscore');
-var Mn = require('backbone.marionette');
-
-export.MyView = Mn.View.extend({
- template: _.template('
Hello, world
')
-});
-```
-
-This doesn't have to be an underscore template, you can pass your own rendering
-function:
-
-```javascript
-var Mn = require('backbone.marionette');
-var Handlebars = require('handlebars');
-
-var MyView = Mn.View.extend({
- template: function(data) {
- return Handlebars.compile('Hello, {{ name }}')(data);
- }
-});
-```
-
-[Live example](https://jsfiddle.net/marionettejs/ep0e4qkt/)
-
-Using a custom function can give you a lot of control over the output of your
-view after its context is calculated. If this logic is common, you may be best
-[overriding your renderer](./marionette.renderer.md) to change your default
-template renderer.
-
-### The `getTemplate` function
-
-The `getTemplate` function is used to choose the template to render after the
-view has been instantiated. You can use this to change the template based on
-some simple logic such as the value of a specific attribute in the view's model.
-The returned value can be either a jQuery selector or a compiled template
-function that will be called with the view's data and context.
-
-```javascript
-var Mn = require('backbone.marionette');
-
-var MyView = Mn.View.extend({
- getTemplate: function(){
- if (this.model.get('is_active')){
- return '#template-when-active';
- } else {
- return '#template-when-inactive';
- }
- }
-});
-```
-
-[Live example](https://jsfiddle.net/marionettejs/9k5v4p92/)
-
-This differs from setting `template` as this method must be executed and
-calculated when the view is rendered. If your template is always the same, use
-the `template` attribute directly.
-
-## Models and Collections
-
-### Rendering a Model
-
-Marionette will happily render a template without a model. This won't give us a
-particularly interesting result. As with Backbone, we can attach a model to our
-views and render the data they represent:
-
-```javascript
-var Bb = require('backbone');
-var Mn = require('backbone.marionette');
-
-var MyModel = Bb.Model.extend({
- defaults: {
- name: 'world'
- }
-});
-
-var MyView = Mn.View.extend({
- template: _.template('
Hello, <%- name %>
')
-});
-
-var myView = new MyView({model: new MyModel()});
-```
-
-[Live example](https://jsfiddle.net/marionettejs/warfa6rL/)
-
-Now our template has full access to the attributes on the model passed into the
-view.
-
-### Rendering a Collection
-
-The `Marionette.View` also provides a simple tool for rendering collections into
-a template. Simply pass in the collection as `collection` and Marionette will
-provide an `items` attribute to render:
-
-```javascript
-var Bb = require('backbone');
-var Mn = require('backbone.marionette');
-
-var MyCollection = Bb.Collection.extend({
-});
-
-var MyView = Mn.View.extend({
- template: '#hello-template'
-});
-
-var collection = new MyCollection([
- {name: 'Steve'}, {name: 'Helen'}
-]);
-
-var myView = new MyView({collection: collection});
-```
-
-For clarity, we've moved the template into this script tag:
-
-```html
-
-```
-
-[Live example](https://jsfiddle.net/marionettejs/qyodkakf/)
-
-As you can see, `items` is provided to the template representing each record in
-the collection.
-
-### User Interaction with Collections
-
-While possible, reacting to user interaction with individual items in your
-collection is tricky with just a `View`. If you want to act on individual items,
-it's recommended that you use [`CollectionView`](./marionette.collectionview.md)
-and handle the behavior at the individual item level.
-
-### Model/Collection Rendering Rules
-
-Marionette uses a simple method to determine whether to make a model or
-collection available to the template:
-
-1. If `view.model` is set, the attributes from `model`
-2. If `view.model` is not set, but `view.collection` is, set `items` to the
- individual items in the collection
-3. If neither are set, an empty object is used
-
-The result of this is mixed into the
-[`templateContext` object](#template-context) and made available to your
-template. Using this means you can setup a wrapper `View` that can act on
-`collectionEvents` but will render its `model` attribute - if your `model` has
-an `items` attribute then that will always be used. If your view needs to serialize
-by different rules override [`serializeData()`](#serializing-model-and-collection-data).
-
-## Template Context
-
-The `Marionette.View` provides a `templateContext` attribute that is used to add
-extra information to your templates. This can be either an object, or a function
-returning an object. The keys on the returned object will be mixed into the
-model or collection keys and made available to the template.
-
-### Context Object
-
-Using the context object, simply attach an object to `templateContext` as so:
-
-```javascript
-var _ = require('underscore');
-var Mn = require('backbone.marionette');
-
-var MyView = Mn.View.extend({
- template: _.template('Hello, <%- contextKey %>
'),
-
- templateContext: {
- contextKey: 'world'
- }
-});
-
-var myView = new MyView();
-```
-
-[Live example](https://jsfiddle.net/marionettejs/rw09r7e6/)
-
-The `myView` instance will be rendered without errors even though we have no
-model or collection - `contextKey` is provided by `templateContext`.
-
-The `templateContext` attribute can also
-[take a function](./basics.md#functions-returning-values).
-
-### Context Function
-
-The `templateContext` object can also be a [function returning an object](basics.md#functions-returning-values).
-This is useful when you want to access [information from the surrounding view](#binding-of-this)
-(e.g. model methods).
-
-To use a `templateContext`, simply assign a function:
-
-```javascript
-var _ = require('underscore');
-var Mn = require('backbone.marionette');
-
-var MyView = Mn.View.extend({
- template: _.template('Hello, <%- contextKey %>
'),
-
- templateContext: function() {
- return {
- contextKey: this.getOption('contextKey')
- }
- }
-});
-
-var myView = new MyView({contextKey: 'world'});
-```
-
-[Live example](https://jsfiddle.net/marionettejs/4qxk99ya/)
-
-Here, we've passed an option that can be accessed from the `templateContext`
-function using `getOption()`. More information on `getOption` can be found in
-the [documentation for `Marionette.Object`](./marionette.object.md#getoption).
-
-### Binding of `this`
-
-When using functions in the `templateContext` it's important to know that `this`
-is _bound to the result of [`serializeData()`](#serializing-model-and-collection-data) and **not the view**_. An
-illustrative example:
-
-```javascript
-var _ = require('underscore');
-var Mn = require('backbone.marionette');
-
-var MyView = Mn.View.extend({
- template: _.template('Hello, <%- contextKey() %>
'),
-
- templateContext: {
- contextKey: function() {
- return this.getOption('contextKey'); // ERROR
- }
- }
-});
-
-var myView = new MyView({contextKey: 'world'});
-```
-
-The above code will fail because the context object in the template
-_cannot see_ the view's `getOption`. This would also apply to functions
-returned by a `templateContext` function, even though the function itself is
-bound to the view context. The following example should provide some clarity:
-
-```javascript
-var _ = require('underscore');
-var Mn = require('backbone.marionette');
-
-var MyView = Mn.View.extend({
- template: _.template('Hello, <%- contextKey() %>
'),
-
- templateContext: function() {
- return {
- contextKeyVar: this.getOption('contextKey'), // OK - "this" refers to view
- contextKeyFunction: function() {
- return this.getOption('contextKey'); // ERROR - "this" refers to data
- }
- };
-
- }
-});
-
-var myView = new MyView({contextKey: 'world'});
-```
-
-[Live example](https://jsfiddle.net/marionettejs/cwt31k9p/1/)
-
-## Serializing Model and Collection Data
-
-The `serializeData` method is used to convert a View's `model` or `collection`
-into a usable form for a template. It follows the [Model/Collection Rendering Rules](#modelcollection-rendering-rules)
-to determine how to serialize the data.
-
-The result of `serializeData` is included in the data passed to
-the view's template.
-
-Let's take a look at some examples of how serializing data works.
-
-```javascript
-var myModel = new MyModel({foo: 'bar'});
-
-new MyView({
- template: '#myItemTemplate',
- model: myModel
-});
-
-MyView.render();
-```
-
-```html
-
-```
-
-[Live example](https://jsfiddle.net/marionettejs/brp0t7pq/)
-
-If the serialization is a collection, the results are passed in as an
-`items` array:
-
-```javascript
-var myCollection = new MyCollection([{foo: 'bar'}, {foo: 'baz'}]);
-
-new MyView({
- template: '#myCollectionTemplate',
- collection: myCollection
-});
-
-MyView.render();
-```
-
-```html
-
-```
-
-[Live example](https://jsfiddle.net/marionettejs/yv3hrvkf/)
-
-If you need to serialize the View's `model` or `collection` in a custom way,
-then you should override either `serializeModel` or `serializeCollection`.
-
-On the other hand, you should not use this method to add arbitrary extra data
-to your template. Instead, use [View.templateContext](./template.md#templatecontext).
diff --git a/docs/upgrade.md b/docs/upgrade.md
index 67892a5389..57c3295e5d 100644
--- a/docs/upgrade.md
+++ b/docs/upgrade.md
@@ -77,7 +77,7 @@ parent. These can be chained all the way up to the level you require them to be.
Bubbled child events no longer pass the `childView` implicitly and only pass the
arguments passed as part of `triggerMethod`. This means that the arguments
passed to `onEvent` and `onChildviewEvent` are now identical. See the
-[documentation on event lifecycles](./viewlifecycle.md) for more information.
+[documentation on event lifecycles](./view.lifecycle.md) for more information.
In Marionette 2, `childEvents` were bound on every event. In Marionette 3,
`childViewEvents` are bound once and cached. This means that you cannot add new
@@ -175,7 +175,7 @@ In Marionette 3, the HTML will be:
The arguments for a number of lifecycle events were changed. For consistency,
all events will now receive the view that is emitting the event as the first
-argument. See the [documentation for view lifecycles](./viewlifecycle.md) for
+argument. See the [documentation for view lifecycles](./view.lifecycle.md) for
more complete information.
#### Upgrading to Marionette 3
@@ -272,7 +272,7 @@ in Marionette to manage the `CollectionView` children.
The main difference between Babysitter and the Marionette implementation is the
removal of `.call` and `.apply` on `CollectionView.children`. Instead you should
use `.invoke` or
-[any of the methods provided](./marionette.collectionviewadvanced.md#collectionview-childview-iterators-and-collection-functions).
+[any of the methods provided](./marionette.collectionview.md#collectionview-childview-iterators-and-collection-functions).
For example:
diff --git a/docs/view.lifecycle.md b/docs/view.lifecycle.md
new file mode 100644
index 0000000000..d95c769112
--- /dev/null
+++ b/docs/view.lifecycle.md
@@ -0,0 +1,211 @@
+# View Lifecycle
+
+Both [`View` and `CollectionView`](./classes.md) are aware of their lifecycle state
+which indicates if the view is rendered, attached to the DOM or destroyed.
+
+## Documentation Index
+
+* [View Lifecycle](#view-lifecycle)
+* [Lifecycle State Methods](#lifecycle-state-methods)
+ * [`isRendered()`](#isrendered)
+ * [`isAttached()`](#isattached)
+ * [`isDestroyed()`](#isdestroyed)
+* [Instantiating a View](#instantiating-a-view)
+ * [Using `setElement`](#using-setelement)
+* [Rendering a View](#rendering-a-view)
+ * [`View` Rendering](#view-rendering)
+ * [`CollectionView` Rendering](#collectionview-rendering)
+* [Rendering Children](#rendering-children)
+* [Attaching a View](#attaching-a-view)
+* [Detaching a View](#detaching-a-view)
+* [Destroying a View](#destroying-a-view)
+* [Destroying Children](#rendering-children)
+
+## Lifecycle State Methods
+
+Both `View` and `CollectionView` share methods for checking lifecycle state.
+
+### `isRendered()`
+
+Returns a boolean value reflecting if the view is considered rendered.
+
+### `isAttached()`
+
+Returns a boolean value reflecting if the view is considered attached to the DOM.
+
+### `isDestroyed()`
+
+Returns a boolean value reflecting if the view has been destroyed.
+
+## Instantiating a View
+
+Marionette Views are Backbone Views and so when they are instantiated the view
+has an `el`. That `el` will be the root node for the view and other than its contents it
+will not change for the life of the view unless directly manipulated (ie: `view.$el.addClass`)
+
+The view can be passed an existing `el` either in the DOM (ie: `el: $('.foo-selector')`)
+or in memory (ie: `el: $('')`) or most commonly, the view constructs
+its own `el` at instantiation as [documented on backbonejs.org](http://backbonejs.org/#View-el).
+
+Marionette will determine the initial state of the view as to whether the view is considered
+already [rendered](#rendering-a-view) or [attached](#attaching-a-view). If a view is already
+rendered or attached its [state](#lifecycle-state-methods) will reflect that status, but the
+[related events](./events.class.md#dom-change-events) will not have fired.
+
+For more information on instanting a view with pre-rendered DOM see: [Prerendered Content](./dom.prerendered.md).
+
+### Using `setElement`
+
+`Backbone.View` allows the user to change the view's `el` after instantiaton using
+[`setElement`](http://backbonejs.org/#View-setElement). This method can be used in Marionette
+as well, but should be done with caution. `setElement` will redelegate view events, but it will
+essentially ignore children of the view, whether through `regions` or through `children` and the
+view's `behaviors` will also be unaware of the change. It is likely better to reconstuct a new
+view with the new `el` than to try to change the `el` of an existing view.
+
+## Rendering a View
+
+In Marionette [rendering a view](./view.rendering.md) is changing a view's `el`'s contents.
+
+What rendering indicates varies slightly between the two Marionette views.
+
+**Note** Once a view is considered "rendered" it cannot be unrendered until it is [destroyed](#destroying-a-view).
+
+### `View` Rendering
+
+For [`View`](./marionette.view.md), rendering entails serializing the view's data, passing it to a template,
+and taking the results of that template and replacing the contents of the view's `el`. So when a `View` is
+instantiated it is considered rendered if the `el` node contains any content. However after instantiation
+a template may render empty in which case the `View` will still be considered "rendered" even though it
+contains no content.
+
+### `CollectionView` Rendering
+
+For [`CollectionView`](./marionette.collectionview.md), rendering signifies that the view's
+[`children`](./marionette.collectionview.md#collectionviews-children) were created and attached to the
+view's `el`. So unlike `View` a `CollectionView` can be instantiated with content in its `el`, but until
+the `children` are "rendered" the entire view is not considered rendered.
+
+Notably if there are no `children` when rendering, the view will still be considered rendered. This is
+true whether or not an [`emptyView`](./marionette.collectionview.md#collectionviews-emptyview) is rendered.
+So it is possible for a `CollectionView` to be "rendered" but the `el` to only be an empty tag.
+Also note that just like `View` a `CollectionView` may have a `template` which is rendered and attached to
+the `el` during the `render`, but the template rendering itself has no bearing on the status of the `CollectionView`.
+
+## Rendering Children
+
+Rendering child views is often best accomplish after the view render as typically the first render happens prior to
+the view entering the DOM. This helps to prevent unnecessary repaints and reflows by making the DOM insert at the
+highest possible view in the view tree.
+
+The exception is views with [prerendered content](./dom.prerendered.md). In the case that the view is instantiated
+rendered, child views are best managed in the view's [`initialize`](./common.md#initialize).
+
+### `View` Children
+
+In general the best method for adding a child view to a `View` is to use [`showChildView`](./marionette.view.md#showing-a-view)
+in the [`render` event](./events.class.md#render-and-beforerender-events).
+
+View regions will be emptied on each render so views shown outside of the `render` event will still need be reshown
+on subsequent renders.
+
+### `CollectionView` Children
+
+The primary use case for a `CollectionView` is maintaining child views to match the state of a Backbone Collection.
+By default children will be added or removed to match the models within the collection.
+However a `CollectionView` can have children in addition to, or instead of, views matching the `collection`.
+
+#### Adding managed children
+
+If you add a view to a `CollectionView`s children by default it will treat it as any other view added from the `collection`.
+This means it is subject to the [`viewComparator`](./marionette.collectionview.md#defining-the-viewcomparator) and
+[`viewFilter`](./marionette.collectionview.md#defining-the-viewfilter).
+
+So if you are accounting for added views in your `viewFilter` and `viewComparator` the best place to add these children is
+likely in the [`render` event](./events.class.md#render-and-beforerender-events) as the views will only be added once
+(or re-added if the children are rebuilt in a subsequent `render`) and managed in the sort or filter as the `collection` is updated.
+
+#### Adding unmanaged children
+
+Unlike managed children there may be cases where you want to insert views to the results of the `CollectionView` after the
+`collection` changes, or after sorting and/or filtering. In these cases the solution might depend slightly on the features
+used on the `CollectionView`.
+
+The goal will be to add the unmanaged views after other views are added and to remove any unmanaged views prior to any
+managed `children` changes. To do so you must understand which [`CollectionView` event](./events.class.md#collectionview-events)
+will occur prior to changes to the `children` for your particular use case. By default a `CollectionView` sorts according
+to the `collection` sort, so unless `viewComparator` is disabled, the best event for removing unmanaged views is the
+[`before:sort` event](./events.class.md#sort-and-beforesort-events), but if `viewComparator` is false the next event
+to consider is the [`before:filter` event](./events.class.md#filter-and-beforefilter-events) if your `CollectionView` has
+a `viewFilter`, otherwise the [`before:render:children` event](./events.class.md#renderchildren-and-beforerenderchildren-events)
+is ideal.
+
+Once you have determined the best strategy for removing your unmanaged child views, adding them is best handled in the
+[`render:children` event](./events.class.md#renderchildren-and-beforerenderchildren-events). Additionally adding a child
+with `addChildView` will itself cause these events to occur, so to prevent stack overflows, it is best to use a flag to guard
+the adds and to insert a new view at a specified index.
+
+The following simplistic example will add an unmanaged view at the 5th index and remove it prior to any changes to the `children`.
+In a real world scenario it will likely be more complicated to keep track of which view to remove in the `onBeforeSort`.
+
+```javascript
+import { CollectionView } from 'backbone.marionette';
+
+const MyCollectionView = CollectionView.extend({
+ childView: MyChildView,
+ onBeforeSort() {
+ this.removeChildView(this.children.findByIndex(5));
+ },
+ onRenderChildren() {
+ this.addFooView();
+ },
+ addFooView() {
+ if (this.addingFooView) {
+ return;
+ }
+
+ this.addingFooView = true;
+ this.addChildView(new FooView(), 5);
+ this.addingFooView = false;
+ }
+});
+```
+
+## Attaching a View
+
+In Marionette a view is attached if the view's `el` can be found in the DOM.
+The best time to add listeners to the view's `el` is likely in the [`attach` event](./events.class.md#attach-and-beforeattach-events).
+
+While the `el` of the view can be attached the contents of the view can be removed and added to
+during the lifetime of the view. If you are adding listeners to the contents of the view rather than
+`attach` the [`dom:refresh` event](./events.class.md#domrefresh-event) would be best.
+
+The attached state is maintained when attaching a view with a `Region` or as a child of a `CollectionView`
+or during [view instantiation](#instantiating-a-view).
+If a view is attached by other means like `$.append` [`isAttached`] may not reflect the actual state of attachment.
+
+## Detaching a View
+
+A view is detached when its `el` is removed from the DOM.
+The best time to clean up any listeners added to the `el` is in the [`before:detach` event](./events.class.md#detach-and-beforedetach-events).
+
+While the `el` of the view may remain attached, its contents will be removed on render.
+If you have added listeners to the contents of the view rather than `before:detach` the
+[`dom:remove` event](./events.class.md#domremove-event) would be best.
+
+## Destroying a View
+
+Destroying a view (ie: `myView.destroy()`) cleans up anything constucted within Marionette so that if
+a view's instance is no longer referenced the view can be cleaned up by the browser's garbage collector.
+
+The [`before:destroy` event](./events.class.md#destroy-and-beforedestroy-events) is the best place to clean
+up any added listeners not related to the view's DOM.
+
+The state of the view after the destroy is not attached and not rendered although the `el` is not emptied.
+
+## Destroying Children
+
+Children added to a `View`'s region or through a `CollectionView` will be automatically destroyed if the
+view is re-rendered, if the view is destroyed, or for `CollectionView` if the `collection` is reset.
+
+**Note** Children are removed after the DOM detach of the parent to prevent multiple reflows or repaints.
diff --git a/docs/view.rendering.md b/docs/view.rendering.md
new file mode 100644
index 0000000000..e6202d77a2
--- /dev/null
+++ b/docs/view.rendering.md
@@ -0,0 +1,490 @@
+# View Template Rendering
+
+Unlike [`Backbone.View`](http://backbonejs.org/#View-template), [Marionette views](./classes.md)
+provide a customizable solution for rendering a template with data and placing the
+results in the DOM.
+
+```javascript
+import _ from 'underscore';
+import { View } from 'backbone.marionette';
+
+const MyView = View.extend({
+ tagName: 'h1',
+ template: _.template('Contents')
+});
+
+const myView = new MyView();
+myView.render();
+```
+
+In the above example the contents of the `template` attribute will be rendered inside
+a `` tag available at `myView.el`.
+
+[Live example](https://jsfiddle.net/marionettejs/h762zjua/)
+
+## Documentation Index
+
+* [What is a template](#what-is-a-template)
+* [Setting a View Template](#setting-a-view-template)
+ * [Using a View Without a Template](#using-a-view-without-a-template)
+* [Rendering the Template](#rendering-the-template)
+ * [Using a Custom Renderer](#using-a-custom-renderer)
+ * [Rendering to HTML](#rendering-to-html)
+ * [Rendering to DOM](#rendering-to-dom)
+* [Serializing Data](#serializing-data)
+ * [Serializing a Model](#serializing-a-model)
+ * [Serializing a Collection](#serializing-a-collection)
+ * [Serializing with a `CollectionView`](#serializing-with-a-collectionview)
+* [Adding Context Data](#adding-context-data)
+ * [What is Context Data?](#what-is-context-data)
+
+## What is a template?
+
+A template is a function that given data returns either an HTML string or DOM.
+[The default renderer](#rendering-the-template) in Marionette expects the template to
+return an HTML string. Marionette's dependency Underscore comes with an HTML string
+[template compiler](http://underscorejs.org/#template).
+
+```javascript
+import _ from 'underscore';
+import { View } from 'backbone.marionette';
+
+const MyView = View.extend({
+ template: _.template('
Hello, world
')
+});
+```
+This doesn't have to be an underscore template, you can pass your own rendering
+function:
+
+```javascript
+import Handlebars from 'handlebars';
+import { View } from 'backbone.marionette';
+
+const MyView = View.extend({
+ template: Handlebars.compile('Hello, {{ name }}')
+});
+```
+
+[Live example](https://jsfiddle.net/marionettejs/ep0e4qkt/)
+
+## Setting a View Template
+
+Marionette views use the `getTemplate` method to determine which template to use for
+rendering into its `el`. By default `getTemplate` is predefined on the view as simply:
+
+```javascript
+getTemplate() {
+ return this.template
+}
+```
+
+In most cases by using the default `getTemplate` you can simply set the `template` on the
+view to define the view's template, but in some circumstances you may want to set the template
+conditionally.
+
+```javascript
+import { View } from 'backbone.marionette';
+
+const MyView = View.extend({
+ template: _.template('Hello World!'),
+ getTemplate() {
+ if (this.model.has('user')) {
+ return _.template('Hello User!');
+ }
+
+ return this.template;
+ }
+});
+```
+
+[Live example](https://jsfiddle.net/marionettejs/9k5v4p92/)
+
+### Using a View Without a Template
+
+By default `CollectionView` has no defined `template` and will only attempt to render the `template`
+if one is defined. For `View` there may be some situations where you do not intend to use a `template`.
+Perhaps you only need the view's `el` or you are using [prerendered content](./dom.prerendered.md).
+
+In this case setting `template` to `false` will prevent the template render. In the case of `View`
+it will also prevent the [`render` events](./events.class.md#render-and-beforerender-events).
+
+```javascript
+import { View } from 'backbone.marionette';
+
+const MyIconButtonView = View.extend({
+ template: false,
+ tagName: 'button',
+ className: '.icon-button',
+ triggers: {
+ 'click': 'click'
+ },
+ onRender() {
+ console.log('You will never see me!');
+ }
+});
+```
+
+## Rendering the Template
+
+Each view class has a renderer which by default passes the [view data](#serializing-data)
+to the template function and returns the html string it generates.
+
+The current default renderer is essentially the following:
+```javascript
+import { View, CollectionView } from 'backbone.marionette';
+
+function renderer(template, data) {
+ return template(data);
+}
+
+View.setRenderer(renderer);
+CollectionView.setRenderer(renderer);
+```
+
+Previous to Marionette v4 the default renderer was the `TemplateCache`. This renderer has been extracted
+to a separate library: https://github.com/marionettejs/marionette.templatecache and can be used with v4.
+
+### Using a Custom Renderer
+
+You can set the renderer for a view class by using the class method `setRenderer`.
+The renderer accepts two arguments. The first is the template passed to the view,
+and the second argument is the data to be rendered into the template.
+
+Here's an example that allows for the `template` of a view to be an underscore template string.
+
+```javascript
+import _ from 'underscore';
+import { View } from 'backbone.marionette';
+
+View.setRenderer(function(template, data) {
+ return _.template(template)(data);
+});
+
+const myView = new View({
+ template: 'Hello <%- name %>!',
+ model: new Backbone.Model({ name: 'World' })
+});
+
+myView.render();
+
+// myView.el is
+ <% _.each(groups, function(group) { %>
+
+ `),
+ serializeData() {
+ // For this view I need both the
+ // model and collection serialized
+ return {
+ user: this.serializeModel(),
+ groups: this.serializeCollection(),
+ };
+ }
+});
+```
+
+**Note** You should not use this method to add arbitrary extra data to your template.
+Instead use `templateContext` to [add context data to your template](#adding-context-data).
+
+### Serializing a Model
+
+If the view has a `model` it will pass that model's attributes
+to the template.
+
+```javascript
+import _ from 'underscore';
+import Backbone from 'backbone';
+import { View } from 'backbone.marionette';
+
+const MyModel = Backbone.Model.extend({
+ defaults: {
+ name: 'world'
+ }
+});
+
+const MyView = View.extend({
+ template: _.template('Hello, <%- name %>
')
+});
+
+const myView = new MyView({ model: new MyModel() });
+```
+
+[Live example](https://jsfiddle.net/marionettejs/warfa6rL/)
+
+How the `model` is serialized can also be customized per view.
+
+```javascript
+import _ from 'underscore';
+import { View } from 'backbone.marionette';
+
+const MyView = View.extend({
+ serializeModel() {
+ const data = _.clone(this.model.attributes);
+
+ // serialize nested model data
+ data.sub_model = data.sub_model.attributes;
+
+ return data;
+ }
+});
+```
+
+### Serializing a Collection
+
+If the view does not have a `model` but has a `collection` the collection's models will
+be serialized to an array provided as an `items` attribute to the template.
+
+```javascript
+import _ from 'underscore';
+import Backbone from 'backbone';
+import { View } from 'backbone.marionette';
+
+const MyView = View.extend({
+ template: _.template(`
+
+ <% _.each(items, function(item) { %>
+
+ `)
+});
+
+const collection = new Backbone.Collection([
+ {name: 'Steve'}, {name: 'Helen'}
+]);
+
+const myView = new MyView({ collection });
+```
+
+[Live example](https://jsfiddle.net/marionettejs/qyodkakf/)
+
+How the `collection` is serialized can also be customized per view.
+
+```javascript
+import _ from 'underscore';
+import { View } from 'backbone.marionette';
+
+const MyView = View.extend({
+ serializeCollection() {
+ return _.map(this.collection.models, model => {
+ const data = _.clone(model.attributes);
+
+ // serialize nested model data
+ data.sub_model = data.sub_model.attributes;
+
+ return data;
+ });
+ }
+});
+```
+
+### Serializing with a `CollectionView`
+
+if you are using a `template` with a `CollectionView` that is not also given a `model`, your `CollectionView`
+will [serialize the collection](serializing-a-collection) for the template. This could be costly and unnecessary.
+If your `CollectionView` has a `template` it is advised to either use an empty `model` or override the
+[`serializeData`](#serializing-data) method.
+
+## Adding Context Data
+
+Marionette views provide a `templateContext` attribute that is used to add
+extra information to your templates. This can be either an object, or a function
+returning an object. The keys on the returned object will be mixed into the
+model or collection keys and made available to the template.
+
+```javascript
+import _ from 'underscore';
+import { View } from 'backbone.marionette';
+
+const MyView = View.extend({
+ template: _.template('Hello, <%- name %>
'),
+ templateContext: {
+ name: 'World'
+ }
+});
+```
+
+Additionally context data overwrites the serialized data
+
+```javascript
+import _ from 'underscore';
+import { View } from 'backbone.marionette';
+
+const MyView = View.extend({
+ template: _.template('Hello, <%- name %>
'),
+ templateContext() {
+ return {
+ name: this.model.get('name').toUpperCase()
+ };
+ }
+});
+```
+
+You can also define a template context value as a method. How this method is called is determined
+by your templating solution. For instance with handlebars a method is called with the context of
+the data passed to the template.
+
+```javascript
+import Handlebars from 'handlebars';
+import Backbone from 'backbone';
+import { View } from 'backbone.marionette';
+
+const MyView = View.extend({
+ template: Handlebars.compile(`
+ Hello {{ fullName }}
,
+ `),
+ templateContext: {
+ isDr() {
+ return (this.degree) === 'phd';
+ },
+ fullName() {
+ // Because of Handlebars `this` here is the data object
+ // passed to the template which is the result of the
+ // templateContext mixed with the serialized data of the view
+ return this.isDr() ? `Dr. { this.name }` : this.name;
+ }
+ }
+});
+
+const myView = new MyView({
+ model: new Backbone.Model({ degree: 'masters', name: 'Joe' });
+});
+```
+
+**Note** the data object passed to the template is not deeply cloned and in some cases is not cloned at all.
+Take caution when modifying the data passed to the template, that you are not also modifying your model's
+data indirectly.
+
+### What is Context Data?
+
+While [serializing data](#serializing-data) deals more with getting the data belonging to the view
+into the template, template context mixes in other needed data, or in some cases, might do extra
+computations that go beyond simply "serializing" the view's `model` or `collection`
+
+```javascript
+import _ from 'underscore'
+import { CollectionView } from 'backbone.marionette';
+import GroupView from './group-view';
+
+const MyCollectionView = CollectionView.extend({
+ tagName: 'div',
+ childViewContainer: 'ul',
+ childView: GroupView,
+ template: _.template(`
+ Hello <% name %> of <% orgName %>
+ Groups:
+
+ `),
+ templateContext() {
+ const user = this.model;
+ const organization = user.getOrganization();
+ const groups = this.collection;
+
+ return {
+ orgName: organization.get('name'),
+ name: user.getFullName(),
+ stats: groups.countBy('type')
+ };
+ }
+})
+```
diff --git a/docs/viewlifecycle.md b/docs/viewlifecycle.md
deleted file mode 100644
index 1d150b6db8..0000000000
--- a/docs/viewlifecycle.md
+++ /dev/null
@@ -1,689 +0,0 @@
-# View Lifecycle
-
-The Marionette views use an event lifecycle, triggering events on any listeners
-to act at different points inside the creation and destruction of views and
-their children.
-
-## Documentation Index
-
-* [`View` Lifecycle](#view-lifecycle)
- * [View Creation Lifecycle](#view-creation-lifecycle)
- * [View Destruction Lifecycle](#view-destruction-lifecycle)
- * [View Creation Events](#view-creation-events)
- * [View Destruction Events](#view-destruction-events)
- * [Other View Events](#other-view-events)
-* [`CollectionView` Lifecycle](#collectionview-lifecycle)
- * [CollectionView Creation Lifecycle](#collectionview-creation-lifecycle)
- * [CollectionView Destruction Lifecycle](#collectionview-destruction-lifecycle)
- * [CollectionView Creation Events](#collectionview-creation-events)
- * [CollectionView Destruction Events](#collectionview-destruction-events)
- * [CollectionView EmptyView Events](#collectionview-emptyview-events)
-* [Lifecycle State Methods](#lifecycle-state-methods)
- * [`isRendered()`](#isrendered)
- * [`isAttached()`](#isattached)
-* [Views associated with previously rendered or attached DOM](#views-associated-with-previously-rendered-or-attached-dom)
-* [`Region`s and the View Lifecycle](#regions-and-the-view-lifecycle)
- * [Show View Events](#show-view-events)
- * [Empty Region Events](#empty-region-events)
-* [Advanced Event Settings](#advanced-event-settings)
-
-## `View` Lifecycle
-Marionette views define a number of events during the creation and destruction
-lifecycle - when the view is displayed in and emptied from a region. In the
-documentation, we will reference the event name, though
-[`onEvent` handling](./events.md#onevent-binding) can be used.
-
-All automatically fired events pass the triggering view to all event handlers as
-the first argument.
-
-### View Creation Lifecycle
-
-When a view is initialized and then displayed inside a region (using
-`showChildView()`) a set of events will be called in a specific order.
-
-| Order | Event | Arguments |
-| :---: |-----------------|------------------------------|
-| 1 | `before:render` | `view` - view being rendered |
-| 2 | `render` | `view` - view being rendered |
-| 3* | `before:attach` | `view` - view being attached |
-| 4* | `attach` | `view` - view being attached |
-| 5* | `dom:refresh` | `view` - view being rendered |
-
-The events marked with "\*" only fire if/when the region's `el` is attached to the DOM.
-
-### View Destruction Lifecycle
-
-When `region.empty()` is called, the view will be destroyed, calling events as
-part of the destruction lifecycle.
-
-| Order | Event | Arguments |
-| :---: |-------------------|-------------------------------------------|
-| 1 | `before:destroy` | `view` - view being destroyed |
-| | | `...args` - arguments passed to `destroy` |
-| 2* | `before:detach` | `view` - view being detached |
-| 3* | `dom:remove` | `view` - view being detached |
-| 4* | `detach` | `view` - view being detached |
-| 5 | `destroy` | `view` - view being destroyed |
-| | | `...args` - arguments passed to `destroy` |
-
-The events marked with "\*" only fire if/when the view was attached to the DOM.
-
-#### ChildView Destruction Lifecycle
-
-The order of the destruction events is dependent on when the view (or a parent view)
-is detached. When a parent attached view is destroyed it will receive the events
-as listed above, but its children will receive both detach events first when the parent
-is detached and the children will be destroyed after the detach is complete.
-
-| Order | Event | Arguments |
-| :---: |-------------------|-------------------------------------------|
-| 1 | `before:detach` | `view` - view being detached |
-| 2* | `dom:remove` | `view` - view being detached |
-| 3* | `detach` | `view` - view being detached |
-| 4* | `before:destroy` | `view` - view being destroyed |
-| | | `...args` - arguments passed to `destroy` |
-| 5 | `destroy` | `view` - view being destroyed |
-| | | `...args` - arguments passed to `destroy` |
-
-The events marked with "\*" only fire if/when the view was attached to the DOM.
-
-### View Creation Events
-
-These events are fired during the view's creation and rendering in a region.
-
-#### View `before:render`
-
-Triggered before a View is rendered.
-
-```javascript
-var Mn = require('backbone.marionette');
-
-Mn.View.extend({
- onBeforeRender: function() {
- // set up final bits just before rendering the view's `el`
- }
-});
-```
-
-#### View `render`
-
-This is the optimal event for handling child views.
-
-Triggered after the view has been rendered.
-You can implement this in your view to provide custom code for dealing
-with the view's `el` after it has been rendered.
-
-```javascript
-var Mn = require('backbone.marionette');
-
-Mn.View.extend({
- onRender: function() {
- console.log('el exists but is not visible in the DOM');
- }
-});
-```
-
-#### View `before:attach`
-
-Triggered after the View has been rendered but just before it is first bound
-into the page DOM. This will only be triggered once per `region.show()`. If
-you are re-rendering your view after it has been shown, you most likely want to
-listen to the `render` or `dom:refresh` events.
-
-#### View `attach`
-
-This is the optimal event to handle when the view's `el` must be in the DOM.
-Clean up any added handlers in [`before:detach`](#view-beforedetach).
-
-Triggered once the View has been bound into the DOM. This is only triggered
-once - the first time the View is attached to the DOM. If you are re-rendering
-your view after it has been shown, you most likely want to listen to the
-`dom:refresh` event.
-
-#### View `dom:refresh`
-
-This is the optimal event to handle when the view's contents must be in the DOM.
-Clean up any added handlers in [`dom:remove`](#view-domremove).
-
-The `dom:refresh` event is fired in two separate places:
-
-1. After the view is attached to the DOM (after the `attach` event)
-2. Every time the `render` method is called
-
-```javascript
-const myView = new Mn.View({
- template: _.template('<%= count %>'),
- templateContext: function() {
- this.count = (this.count || 0) + 1;
- return {
- count: this.count
- };
- },
-
- onRender: function() {
- console.log('render');
- },
-
- onAttach: function() {
- console.log('attach');
- },
-
- onDomRefresh: function() {
- console.log('dom:refresh');
- }
-});
-
-// some layout view
-layout.showChildView('myRegion', myView);
-/*
- Output:
- render
- attach
- dom:refresh
-*/
-
-myView.render();
-/*
- Output:
- render
- dom:refresh
-*/
-```
-
-### View Destruction Events
-
-These events are fired during the view's destruction and removal from a region.
-
-#### View `before:destroy`
-
-Triggered just prior to destroying the view, when the view's `destroy()` method has been called.
-The view may or may not be in the DOM at this point.
-
-```javascript
-var Mn = require('backbone.marionette');
-
-Mn.View.extend({
- onBeforeDestroy: function() {
- // custom destroying and non-DOM related cleanup goes here
- }
-});
-```
-
-#### View `before:detach`
-
-This is the optimal event for cleaning up anything added in [`onAttach`](#view-attach).
-
-The `View` will trigger the `before:detach` event when the view is rendered and
-is about to be removed from the DOM.
-If the view has not been attached to the DOM, this event will not be fired.
-
-```javascript
-var Mn = require('backbone.marionette');
-
-Mn.View.extend({
- onBeforeDetach: function() {
- // custom destroying and DOM related cleanup goes here
- }
-});
-```
-
-#### View `detach`
-
-The `View` will trigger the `detach` event when the view was rendered and has
-just been removed from the DOM.
-
-#### View `dom:remove`
-
-This is the optimal event for cleaning up anything added in [`onDomRefresh`](#view-domrefresh).
-
-The `dom:remove` event is fired in two separate places:
-
-1. Before the view is detached from the DOM (after the `before:detach` event)
-2. Each time the `render` method is called if the view is already rendered.
-
-```javascript
-const myView = new Mn.View({
- template: _.template('<%= count %>'),
- templateContext: function() {
- this.count = (this.count || 0) + 1;
- return {
- count: this.count
- };
- },
-
- onBeforeRender: function() {
- console.log('before:render');
- },
-
- onRender: function() {
- console.log('render');
- },
-
- onBeforeDetach: function() {
- console.log('before:detach');
- },
-
- onDetach: function() {
- console.log('detach');
- },
-
- onDomRemove: function() {
- console.log('dom:remove');
- }
-});
-
-// some layout view
-layout.showChildView('myRegion', myView);
-
-myView.render();
-/*
- Output:
- before:render
- dom:remove
- render
-*/
-
-myView.destroy();
-/*
- Output:
- before:detach
- dom:remove
- detach
-*/
-```
-
-#### View `destroy`
-
-Triggered just after the view has been destroyed. At this point, the view has
-been completely removed from the DOM.
-
-### Other View Events
-
-These events are fired on specific actions performed on the view.
-
-#### View `before:add:region`
-
-When you add a region to a view - through `addRegion()` - the
-`before:add:region` event will fire just before the region is actually added.
-
-#### View `add:region`
-
-When a region is added to a view - through `addRegion()` - the `add:region`
-event will be fired. This happens just after the region is added and is
-available to use on the view.
-
-```javascript
-var Mn = require('backbone.marionette');
-
-var MyView = Mn.View.extend({
- onAddRegion: function(name, region) {
- console.log('Region called ' + name + ' was added');
- }
-});
-
-var myView = new MyView();
-myView.addRegion('regionName', '#selector');
-```
-
-#### View `before:remove:region`
-
-The `View` will trigger a `before:remove:region`
-event before a region is removed from the view.
-This allows you to perform any cleanup operations before the region is removed.
-
-```javascript
-var Mn = require('backbone.marionette');
-
-var MyView = Mn.View.extend({
- onBeforeRemoveRegion: function(name) {
- console.log('Region called ' + name + ' is about to be removed');
- },
-
- regions: {
- regionName: '#selector'
- }
-});
-
-var myView = new MyView();
-myView.removeRegion('foo');
-```
-
-#### View `remove:region`
-
-The `View` will trigger a `remove:region`
-event when a region is removed from the view.
-This allows you to use the region instance one last
-time, or remove the region from an object that has a
-reference to it:
-
-```javascript
-var Mn = require('backbone.marionette');
-
-var view = new Mn.View();
-
-view.on('remove:region', function(name, region) {
- // add the region instance to an object
- delete myObject[name];
-});
-
-view.addRegion('foo', '#bar');
-
-view.removeRegion('foo');
-```
-
-## `CollectionView` Lifecycle
-
-The `CollectionView` has its own lifecycle around the standard `View` event
-rendering lifecycle. This section covers the events that get triggered and what
-they indicate.
-
-### CollectionView Creation Lifecycle
-
-The `CollectionView` creation lifecycle can go down two paths depending on
-whether the collection is populated or empty. The below table shows the order of
-rendering events firing:
-
-| Order | Event | Arguments |
-| :---: |--------------------------|---------------------------------------------------|
-| 1* | `before:render` | `collectionview` - collection view being rendered |
-| 2 | `before:remove:child` | `collectionview` - collection view being rendered |
-| | | `child` - child view being rendered |
-| 3 | `remove:child` | `collectionview` - collection view being rendered |
-| | | `child` - child view being rendered |
-| 4 | `before:add:child` | `collectionview` - collection view being rendered |
-| | | `child` - child view being rendered |
-| 5 | `add:child` | `collectionview` - collection view being rendered |
-| | | `child` - child view being rendered |
-| | | `view` - empty view being rendered |
-| 6+ | `before:sort ` | `collectionview` - collection view being rendered |
-| 7 | `sort` | `collectionview` - collection view being rendered |
-| 8# | `before:filter` | `collectionview` - collection view being rendered |
-| 9# | `filter` | `collectionview` - collection view being rendered |
-| 10 | `before:render:children` | `collectionview` - collection view being rendered |
-| 11 | `render:children` | `collectionview` - collection view being rendered |
-| 12* | `render` | `collectionview` - collection view being rendered |
-| 13** | `before:attach` | `collectionview` - collection view being rendered |
-| 14** | `attach` | `collectionview` - collection view being rendered |
-| 15** | `dom:refresh` | `collectionview` - collection view being rendered |
-
-"\*" only fire if the `CollectionView` is fully rendering from either `collectionView.render()` or `collectionView.collection.reset()`.
-"+" including and after this point only occur if there are some children to render.
-"#" only fires if a `viewFilter` is defined.
-"\*\*" fires from use in the parent when a CollectionView is shown in a Region or
-as a childView of another CollectionView.
-
-
-### CollectionView Destruction Lifecycle
-
-When a `CollectionView` is destroyed it fires a series of events in order to
-reflect the different stages of the destruction process.
-
-| Order | Event | Arguments |
-| :---: |------------------------------|-----------------------------------------------------|
-| 1 | `before:destroy` | `collectionview` - collection view being destroyed |
-| | | `...args` - arguments passed to `destroy` |
-| 2 | `before:detach` | `collectionview` - collection view being destroyed |
-| 3 | `detach` | `collectionview` - collection view being destroyed |
-| 4 | `before:destroy:children` | `collectionview` - collection view being destroyed |
-| 5+ | `before:remove:child` | `collectionview` - collection view being destroyed |
-| | | `view` - child view being destroyed |
-| 6+ | `remove:child` | `collectionview` - collection view being destroyed |
-| | | `view` - child view being destroyed |
-| 7 | `destroy:children` | `collectionview` - collection view being destroyed |
-| 8 | `destroy` | `collectionview` - collection view being destroyed |
-| | | `...args` - arguments passed to `destroy` |
-
-The events marked with "+" only fire on collections with children.
-
-### CollectionView Creation Events
-
-#### CollectionView `before:render`
-
-Triggers before the `CollectionView` render process starts. See the
-[`before:render` Documentation](#view-before-render) for an
-example.
-
-#### CollectionView `before:add:child`
-
-This event fires before each child is added to the children.
-
-#### CollectionView `add:child`
-
-This event fires after each child is added to the children. This fires once for each
-item in the attached collection.
-
-#### CollectionView `before:sort`
-
-This event fires just before sorting the children in the `CollectionView`.
-By default this only fires if the collectionView has at least one child.
-
-#### CollectionView `sort`
-
-This event fires after sorting the children in the `CollectionView`.
-By default this only fires if the collectionView has at least one child.
-
-#### CollectionView `before:filter`
-
-This event fires just before filtering the children in the `CollectionView`.
-By default this only fires if the collectionView has at least one child and has a `viewFilter`.
-
-#### CollectionView `filter`
-
-This event fires after filtering the children in the `CollectionView`.
-By default this only fires if the collectionView has at least one child and has a `viewFilter`.
-
-#### CollectionView `before:render:children`
-
-This event fires just before rendering the children in the `CollectionView`.
-By default this only fires if the collectionView has at least one view not filtered out.
-
-#### CollectionView `render:children`
-
-This event fires once all the collection's child views have been rendered.
-By default this only fires if the collectionView has at least one view not filtered out.
-
-```
-var Bb = require('backbone');
-var Mn = require('backbone.marionette');
-
-var MyView = Mn.CollectionView.extend({
- onRenderChildren: function({
- console.log('The collectionview children have been rendered');
- })
-});
-
-var myView = new MyView({
- collection: new Bb.Collection([{ id: 1 }]);
-});
-
-myView.render();
-```
-
-#### CollectionView `render`
-
-Fires when the collection has completely finished rendering. See the
-[`render` Documentation](#view-render) for more information.
-
-### CollectionView Destruction Events
-
-#### CollectionView `before:destroy`
-
-Fires as the destruction process is beginning. This is best used to perform any
-necessary cleanup within the `CollectionView`.
-
-```javascript
-var Mn = require('backbone.marionette');
-
-var MyView = Mn.CollectionView.extend({
- onBeforeDestroy: function() {
- console.log('The CollectionView is about to be destroyed');
- }
-});
-
-var myView = new MyView();
-
-myView.destroy();
-```
-
-#### CollectionView `before:detach`
-
-Fires just before the `CollectionView` is removed from the DOM. If you need to
-remove any event handlers or UI modifications, this would be the best time to do
-that.
-
-#### CollectionView `detach`
-
-Fires just after the `CollectionView` is removed from the DOM. The view's
-elements will still exist in memory if you need to access them.
-
-#### CollectionView `before:destroy:children`
-
-This is triggered just before the `childView` items are destroyed.
-
-Triggered when the `CollectionView` is destroyed or before the `CollectionView`'s children are re-rendered.
-
-#### CollectionView `before:remove:child`
-
-This is triggered for each `childView` that is removed from the
-`CollectionView`. This can *only* fire if the `collection` contains items.
-
-Each item in the `CollectionView` will undergo the
-[destruction lifecycle](#view-destruction-lifecycle)
-
-#### CollectionView `remove:child`
-
-Fired for each view that is removed from the `CollectionView`. This can only
-fire if the `collection` has items.
-
-#### CollectionView `destroy:children`
-
-This is triggered just after the `childView` items are destroyed.
-
-Triggered when the `CollectionView` is destroyed or before the `CollectionView`'s children are re-rendered.
-
-#### CollectionView `destroy`
-
-Fired once the `CollectionView` has been destroyed and no longer exists.
-
-### CollectionView EmptyView Events
-
-The `CollectionView` uses a region internally that can be used to know when the empty view is show or destroyed.
-
-```javascript
-var Mn = require('backbone.marionette');
-
-var MyView = Mn.CollectionView.extend({
- emptyView: MyEmptyView
-});
-
-var myView = new MyView();
-
-myView.emptyRegion.on({
- show: function() {
- console.log('CollectionView is empty!');
- },
- empty: function() {
- console.log('CollectionView is removing the emptyView');
- }
-});
-
-myView.render();
-```
-
-## Lifecycle State Methods
-
-Both `View` and `CollectionView` share methods for checking if the view is attached or rendered.
-
-### `isRendered()`
-
-This function will return a boolean value reflecting if the view has been rendered.
-
-### `isAttached()`
-
-This function will return a boolean value reflecting if the view believes it is attached.
-This is maintained when attaching a view with a `Region` or during [View instantiation](#views-associated-with-previously-rendered-or-attached-dom).
-If a view is attached by other means this function may not reflect the actual state of attachment.
-
-## Views associated with previously rendered or attached DOM
-
-When a view is instantiated, if the View's `el` is set to an existing node
-the view's [`isRendered()`](#isrendered) will return `true` and `before:render`
-and `render` events will not be fired when the view is shown in a Region.
-
-Similarly if the `el` is attached to a node in the DOM the view's [`isAttached()`](#isattached)
-will return `true` and `before:attach`, `attach` and `dom:refresh` will not be fired
-when the view is shown in a Region.
-
-## `Region`s and the View Lifecycle
-
-When you show a view inside a region - either using [`region.show(view)`](./marionette.region.md#showing-a-view) or
-[`showChildView('region', view)`](./marionette.view.md#showing-a-view) - the `Region` will emit events around the view
-events that you can hook into.
-
-### Show View Events
-
-When showing a view inside a region, the region emits a number of events:
-
-| Order | Event | Arguments |
-| :---: |--------------------------------------------|-------------------------------------------|
-| 1 | `before:show` | `region` - region showing the child view |
-| | | `view` - view being shown in the region |
-| | | `options` - options passed to `show()` |
-| 2 | [View Creation Lifecycle](#view-creation-lifecycle) | |
-| 3 | `show` | `region` - region showing the child view |
-| | | `view` - view being shown in the region |
-| | | `options` - options passed to `show()` |
-
-#### Region `before:show`
-
-Emitted on `region.show(view)`, before the view lifecycle begins. At this point,
-none of the view rendering will have been performed.
-
-#### Region `show`
-
-Emitted after the view has been rendered and attached to the DOM (if this
-region is already attached to the DOM). This can be used to handle any
-extra manipulation that needs to occur.
-
-### Empty Region Events
-
-When [emptying a region](./marionette.region.md#emptying-a-region), it will emit destruction events around the view's
-destruction lifecycle:
-
-| Order | Event | Arguments |
-| :---: |-----------------------------------------------|---------------------------------|
-| 1 | `before:empty` | `region` - region being emptied |
-| | | `view` - view being removed |
-| 2 | [View Destruction Lifecycle](#view-destruction-lifecycle) | |
-| 3 | `empty` | `region` - region being emptied |
-| | | `view` - view being removed |
-
-#### Region `before:empty`
-
-Emitted before the view's destruction process begins. This can occur either by
-calling `region.empty()` or by running `region.show(view)` on a region that's
-displaying another view. It will also trigger if the view in the region is
-destroyed.
-
-#### Region `empty`
-
-Fired after the entire destruction process is complete. At this point, the view
-has been removed from the DOM completely.
-
-
-## Advanced Event Settings
-Marionette is able to trigger `attach`/`detach` events down the view tree along with
-triggering the `dom:refresh`/`dom:remove` events because of the view event monitor.
-This monitor starts when a view is created or shown in a region (to handle non-Marionette views).
-
-In some cases it may be a useful performance improvement to disable this functionality.
-Doing so is as easy as setting `monitorViewEvents: false` on the view class.
-
-```javascript
-const NonMonitoredView = Mn.View.extend({
- monitorViewEvents: false
-});
-```
-
-**Important Note**: Disabling the view monitor will break the monitor generated
-events for this view _and all child views_ of this view. Disabling should be done carefully.
diff --git a/readme.md b/readme.md
index f8b88eabe8..c06e610da7 100644
--- a/readme.md
+++ b/readme.md
@@ -15,7 +15,7 @@
- +
## Marionette v3 @@ -62,8 +62,7 @@ to engage in an all-or-nothing migration to begin using Marionette. ### Chat with us -Find us [on gitter](https://gitter.im/marionettejs/backbone.marionette) or on -IRC in the FreeNode.net [#marionette channel](http://freenode.net). +Find us [on gitter](https://gitter.im/marionettejs/backbone.marionette). We're happy to discuss design patterns and learn how you're using Marionette. diff --git a/src/behavior.js b/src/behavior.js index fe4a00b621..5102a19237 100644 --- a/src/behavior.js +++ b/src/behavior.js @@ -32,14 +32,13 @@ const Behavior = function(options, view) { this._setOptions(options, ClassOptions); this.cid = _.uniqueId(this.cidPrefix); - // Construct an internal UI hash using - // the behaviors UI hash and then the view UI hash. - // This allows the user to use UI hash elements - // defined in the parent view as well as those - // defined in the given behavior. + // Construct an internal UI hash using the behaviors UI + // hash combined and overridden by the view UI hash. + // This allows the user to use UI hash elements defined + // in the parent view as well as those defined in the behavior. // This order will help the reuse and share of a behavior - // between multiple views, while letting a view override a - // selector under an UI key. + // between multiple views, while letting a view override + // a selector under an UI key. this.ui = _.extend({}, _.result(this, 'ui'), _.result(view, 'ui')); // Proxy view triggers diff --git a/src/collection-view.js b/src/collection-view.js index 8e7a0d7f38..aad33c98fc 100644 --- a/src/collection-view.js +++ b/src/collection-view.js @@ -310,7 +310,7 @@ const CollectionView = Backbone.View.extend({ throw new MarionetteError({ name: classErrorName, message: `The specified "childViewContainer" was not found: ${childViewContainer}`, - url: 'marionette.collectionview.html#collectionviews-childviewcontainer' + url: 'marionette.collectionview.html#defining-the-childviewcontainer' }); } }, @@ -452,7 +452,8 @@ const CollectionView = Backbone.View.extend({ throw new MarionetteError({ name: classErrorName, - message: '"viewFilter" must be a function, predicate object literal, a string indicating a model attribute, or falsy' + message: '"viewFilter" must be a function, predicate object literal, a string indicating a model attribute, or falsy', + url: 'marionette.collectionview.html#defining-the-viewfilter' }); }, @@ -615,7 +616,7 @@ const CollectionView = Backbone.View.extend({ throw new MarionetteError({ name: classErrorName, message: 'Both views must be children of the collection view to swap.', - url: 'marionette.collectionviewadvanced.html#collectionviews-swapchildviews' + url: 'marionette.collectionview.html#swapping-child-views' }); } diff --git a/src/common/bind-events.js b/src/common/bind-events.js index a65fee62ad..8e5c1af9d5 100644 --- a/src/common/bind-events.js +++ b/src/common/bind-events.js @@ -21,7 +21,7 @@ function normalizeBindings(context, bindings) { if (!_.isObject(bindings)) { throw new MarionetteError({ message: 'Bindings must be an object.', - url: 'marionette.functions.html#marionettebindevents' + url: 'common.html#bindevents' }); } diff --git a/src/common/bind-requests.js b/src/common/bind-requests.js index 1991bb3cef..3ba684aa0b 100644 --- a/src/common/bind-requests.js +++ b/src/common/bind-requests.js @@ -19,7 +19,7 @@ function normalizeBindings(context, bindings) { if (!_.isObject(bindings)) { throw new MarionetteError({ message: 'Bindings must be an object.', - url: 'marionette.functions.html#marionettebindrequests' + url: 'common.html#bindrequests' }); } diff --git a/src/mixins/radio.js b/src/mixins/radio.js index 4598257294..f54e42a533 100644 --- a/src/mixins/radio.js +++ b/src/mixins/radio.js @@ -20,7 +20,7 @@ export default { if (!Radio) { throw new MarionetteError({ message: 'The dependency "backbone.radio" is missing.', - url: 'backbone.radio.html#backbone-radio' + url: 'backbone.radio.html#marionette-integration' }); } diff --git a/src/mixins/regions.js b/src/mixins/regions.js index d6544124a2..0a728c7a39 100644 --- a/src/mixins/regions.js +++ b/src/mixins/regions.js @@ -133,11 +133,11 @@ export default { return this._regions[name]; }, - // Get all regions _getRegions() { return _.clone(this._regions); }, + // Get all regions getRegions() { if (!this._isRendered) { this.render(); diff --git a/src/mixins/view.js b/src/mixins/view.js index 6aa64a6467..0f2efb130a 100644 --- a/src/mixins/view.js +++ b/src/mixins/view.js @@ -65,6 +65,7 @@ const ViewMixin = { return this; }, + // Allows Backbone.View events to utilize `@ui.` selectors _getEvents(events) { if (events) { return this.normalizeUIKeys(events); @@ -154,6 +155,7 @@ const ViewMixin = { this.Dom.detachEl(this.el, this.$el); }, + // This method binds the elements specified in the "ui" hash bindUIElements() { this._bindUIElements(); this._bindBehaviorUIElements(); diff --git a/src/region.js b/src/region.js index b7d0784be7..be056c133a 100644 --- a/src/region.js +++ b/src/region.js @@ -60,9 +60,8 @@ _.extend(Region.prototype, CommonMixin, { // This is a noop method intended to be overridden initialize() {}, - // Displays a backbone view instance inside of the region. Handles calling the `render` - // method for you. Reads content directly from the `el` attribute. The `preventDestroy` - // option can be used to prevent a view from the old view being destroyed on show. + // Displays a view instance inside of the region. If necessary handles calling the `render` + // method for you. Reads content directly from the `el` attribute. show(view, options) { if (!this._ensureElement(options)) { return; @@ -263,8 +262,8 @@ _.extend(Region.prototype, CommonMixin, { this.Dom.appendContents(this.el, view.el, {_$el: this.$el, _$contents: view.$el}); }, - // Destroy the current view, if there is one. If there is no current view, it does - // nothing and returns immediately. + // Destroy the current view, if there is one. If there is no current view, + // it will detach any html inside the region's `el`. empty(options = { allowMissingEl: true }) { const view = this.currentView; @@ -308,6 +307,7 @@ _.extend(Region.prototype, CommonMixin, { this._parentView.stopListening(view); }, + // Non-Marionette safe view.destroy destroyView(view) { if (view._isDestroyed) { return view; @@ -317,6 +317,8 @@ _.extend(Region.prototype, CommonMixin, { return view; }, + // Override this method to determine what happens when the view + // is removed from the region when the view is not being detached removeView(view) { this.destroyView(view); }, @@ -385,6 +387,8 @@ _.extend(Region.prototype, CommonMixin, { return this._isDestroyed; }, + // Destroy the region, remove any child view + // and remove the region from any associated view destroy(options) { if (this._isDestroyed) { return this; } diff --git a/src/view.js b/src/view.js index 51f939c316..6abc0b7e6a 100644 --- a/src/view.js +++ b/src/view.js @@ -35,7 +35,7 @@ function childReducer(children, region) { } // The standard view. Includes view events, automatic rendering -// of Underscore templates, nested views, and more. +// templates, nested views, and more. const View = Backbone.View.extend({ constructor(options) { @@ -69,13 +69,8 @@ const View = Backbone.View.extend({ return this; }, - // Render the view, defaulting to underscore.js templates. - // You can override this in your view definition to provide - // a very specific rendering for your view. In general, though, - // you should override the `Marionette.Renderer` object to - // change how Marionette renders views. - // Subsequent renders after the first will re-render all nested - // views. + // If a template is available, renders it into the view's `el` + // Re-inits regions and binds UI. render() { const template = this.getTemplate();