[![pipeline status][shield-pipeline]][build] [![latest version on npm][shield-npm]][npm] [![code hosted on GitLab][shield-gitlab]][code] [![issue tracker on GitLab][shield-issues]][tracker] [![author: Julian Gonggrijp][shield-jgonggrijp]][jgonggrijp] [![license text][shield-license]][license]

[code]: https://gitlab.com/jgonggrijp/backbone-fractal
[tracker]: https://gitlab.com/jgonggrijp/backbone-fractal/issues
[license]: https://gitlab.com/jgonggrijp/backbone-fractal/blob/master/LICENSE.txt
[build]: https://gitlab.com/jgonggrijp/backbone-fractal/commits/develop
[npm]: https://www.npmjs.com/package/backbone-fractal
[jgonggrijp]: https://juliangonggrijp.com


# backbone-fractal

Lightweight composite views for Backbone.

> **Fractal:** a pattern that repeats in a self-similar way at different scales, such as the branches of a tree or a river, the bumpy contours of a mountain or the spiral sections of a snail’s house.

- [Introduction](#introduction)
    - [A motivating example](#a-motivating-example)
    - [CompositeView](#introducing-compositeview)
    - [CollectionView](#introducing-collectionview)
    - [Conclusion](#conclusion)
- [Installation](#installation)
- [Comparison with Marionette](#comparison-with-marionette)
- [Reference](#reference)
- [Recipes](#recipes)
    - [Pagination](#pagination)
    - [Mixins](#mixins)
    - [Rendering subviews automatically with the parent](#rendering-subviews-automatically-with-the-parent)
    - [Custom elements](#custom-elements)
    - [Chimera views](#chimera-views)


## Introduction

Very often, it is wise to compose a view out of smaller subviews. Doing so has two main benefits over creating a single monolithic view: modularity and performance. However, it can be tricky to get this right with plain `Backbone.View`s. backbone-fractal offers two base classes, `CompositeView` and `CollectionView`, which make this much easier. By using these base classes, you always get the best out of composition:

- A short, declarative syntax.
- Guaranteed correctness: no memory leaks, no dangling references.
- Top efficiency: a parent view can render independently of its subviews.

Below, we illustrate the challenges with composing `Backbone.View`s, as well as the way in which backbone-fractal solves them.


### A motivating example

We start by considering the following monolithic `SearchForm` view.

```js
import { View } from 'backbone';

class SearchForm extends View {
    template({placeholder}) {
        return `
            <input placeholder="${placeholder}">
            <button>Submit</button>
        `;
    }
    render() {
        this.$el.html(this.template(this.model.toJSON()));
        return this;
    }
}

SearchForm.prototype.tagName = 'form';
```

This view is a `<form>` that contains an `<input>` with a placeholder and a `<button>` to submit. The placeholder is set by passing the view a model that has a `placeholder` attribute.

While the above code is straightforward, there are a couple of problems with it:

- We will probably create other form views, such as a `LoginForm`, which also have a `<button>` to submit. Some will have an `<input>` with a placeholder as well. With the above approach, we are going to repeat the HTML for those elements over and over, as well as their event handlers.
- We probably want all submit buttons in our application to look and behave in the same way. If we repeat the code for the submit button in many places, we will have to edit all those places every time when we decide to change something about our submit buttons.
- When the placeholder text changes, we need to re-render the entire search form, even though nothing has changed about the submit button. While the inefficiency may not seem like a big deal in this example, it will quickly add up in a real application with more complicated views.

So our `SearchForm` is inefficient and it lacks modularity. We need to factor out the `<input>` and the `<button>` into separate views, like this:

```js
class QueryField extends View {
    render() {
        this.$el.prop('placeholder', this.model.get('placeholder'));
        return this;
    }
}
QueryField.prototype.tagName = 'input';

class SubmitButton extends View {
    render() {
        this.$el.text('Submit');
        return this;
    }
}
SubmitButton.prototype.tagName = 'button';
```

This is excellent! We just created two very simple views that we can reuse everywhere in our app. If we change something about our submit buttons, we only need to edit the `SubmitButton` class. Now, our first attempt at composing `SearchForm` out of these views might look like this:

```js
class SearchForm extends View {
    render() {
        let input = new QueryField({model: this.model});
        let button = new SubmitButton();
        this.$el.html(''); // clear any previous contents
        input.render().$el.appendTo(this.el);
        button.render().$el.appendTo(this.el);
        return this;
    }
}
```

This works and we solved the modularity problem, but this is still inefficient. We are always re-rendering everything, even the submit button that never changes. To make things worse, we have now introduced a memory leak. We are creating new instances of `QueryField` and `SubmitButton` every time we call `.render()` on the `SearchForm`, but we never call `.remove()` on those instances.

We can address these issues by rewriting the `SearchForm` class again.

```js
class SearchForm extends View {
    initialize() {
        this.input = new QueryField({model: this.model});
        this.button = new SubmitButton();
        this.input.render();
        this.button.render();
    }

    render() {
        this.input.$el.detach();
        this.button.$el.detach();
        this.$el.html(''); // could create container HTML here
        this.input.$el.appendTo(this.el);
        this.button.$el.appendTo(this.el);
        return this;
    }

    remove() {
        this.input.remove();
        this.button.remove();
        return super.remove();
    }
}
```

This is starting to look unwieldly. We will address that next, but let’s first consider what we have gained.

- We have turned the query field and the submit button into permanent members of the search form. This enables us to render them once when we create the search form and then only re-render them when they actually need to change. It also enables us to neatly clean them up when we don’t need the search form anymore.
- When rendering the search form, we detach the subviews before overwriting the HTML of the parent view. Otherwise, the `.el` members of the subviews would become dangling references to destroyed elements as soon as we render the search form for the second time. By detaching the subviews, overwriting the HTML and then re-inserting the subviews again, the search form can have arbitrary unique HTML of its own, in addition to the HTML that the subviews contribute. We will illustrate this later.
- We have three views, the query field, the submit button and the search form, that can all render completely independently of each other, even though one of them is composed out of the other two.

So we have finally arrived at a solution that is both more modular and more efficient than what we started with. Now we just have to do something about the fact that the `SearchForm` class is rather long for a view that is composed out of two smaller ones. The `render` and `remove` methods follow a pattern that will look the same for all views that we compose in this way. This is where backbone-fractal comes in.


### Introducing CompositeView

The following code is equivalent to our last version of `SearchForm`.

```js
import { CompositeView } from 'backbone-fractal';

class SearchForm extends CompositeView {
    initialize() {
        this.input = new QueryField({model: this.model});
        this.button = new SubmitButton();
        this.input.render();
        this.button.render();
    }
}

SearchForm.prototype.subviews = ['input', 'button'];
```

We just derive from `CompositeView` instead of Backbone’s `View` and then declaratively list its subviews. The `CompositeView` class then infers the correct implementation of the `render` and `remove` methods for us.

As mentioned before, the search form may define some unique HTML of its own. `CompositeView` lets us render this with the `renderContainer` method. We may also want to insert the subviews in a nested element instead of the parent view’s root element. We can specify this by passing a more elaborate description to the `subviews` member. Both features are illustrated below.

```js
class SearchForm extends CompositeView {
    // initialize is exactly the same as above
    initialize() {
        this.input = new QueryField({model: this.model});
        this.button = new SubmitButton();
        this.input.render();
        this.button.render();
    }

    template({title}) {
        return `
            <h1>${title}</h1>
            <div class="container"></div>
        `;
    }

    renderContainer() {
        this.$el.html(this.template(this.model.toJSON()));
        return this;
    }
}

SearchForm.prototype.subviews = [{
    view: 'input',
    selector: '.container',
}, {
    view: 'button',
    selector: '.container',
}];
```

The `SearchForm` class just defines how to render its own HTML and the `CompositeView` parent class understands that it needs to insert the subviews in the nested `div.container` element. There are lots of customization options; you can read all about them in the [reference](#reference).

This example illustrates really well why we may want the search form and the query field to re-render independently from each other. While they share the same model, the search form only needs to refresh when the title changes and the query field only needs to refresh when the placeholder changes.


### Introducing CollectionView

Besides `CompositeView`, backbone-fractal provides one other base class, `CollectionView`. It is meant for those situations where a view should represent an entire collection by representing each model in the collection with a separate subview. As an example, the following view classes represent a collection of books as a list of hyperlinks to the books.

```js
import { CollectionView } from 'backbone-fractal';

class BookListItem extends View {
    template({url, title}) {
        return `<a href="${url}">${title}</a>`;
    }
    render() {
        this.$el.html(this.template(this.model.toJSON()));
        return this;
    }
}
BookLink.prototype.tagName = 'li';

class LibraryListing extends CollectionView {
    initialize() {
        this.initItems().initCollectionEvents();
    }
}
LibraryListing.prototype.subview = BookListItem;
LibraryListing.prototype.tagName = 'ol';
```

The above definition of `LibraryListing` is very short, but it is enough for the `CollectionView` class to represent each book in the `LibraryListing`’s `.collection` as a separate `BookListItem`. It will keep the subviews in sync with the collection at all times, adding and removing views as models are added and removed, and keeping the `<ol>` in the same order as the collection.

As with `CompositeView`, `CollectionView` can have a `renderContainer` method. It also has lots of other customization options. For the full details, head over to the [reference](#reference).


### Conclusion

By now, we have seen how `CompositeView` and `CollectionView` offer a short and declarative syntax, correctness and efficiency. Using backbone-fractal, it is easy to get the best out of composing views.

A few more notes. Firstly, you can treat `CompositeView` and `CollectionView` subclasses just like a regular `Backbone.View` subclass. This means you can also nest them inside each other, as deeply as you want. You are encouraged to do this, so you get maximum modularity and efficiency throughout your application.

Secondly, `CompositeView` and `CollectionView` serve the same purposes for nested document structures as tuples and arrays for nested data structures, respectively. This means that together, they are *structurally complete*: they can support every conceivable document structure. Our recipe for [chimera views](#chimera-views) covers the most exotic cases.


## Installation

```console
$ npm install backbone-fractal
```

The library is fully compatible with both Underscore and Lodash, so you can use either. The minimum required versions of the dependencies are as follows:

 - Backbone 1.4
 - Underscore 1.8 or Lodash 4.17
 - jQuery 3.3

For those who use TypeScript, the library includes type declaration files. You don’t need to install any additional `@types/` packages.

If you wish to load the library from a CDN in production (for example via [exposify][exposify]): like all NPM packages, backbone-fractal is available from [jsDelivr][jsDelivr-pkg] and [unpkg][unpkg-pkg]. Be sure to use the `backbone-fractal.js` from the package root directory. It should be embedded after Underscore, jQuery and Backbone and before your own code. It will expose its namespace as `window.BackboneFractal`. Please note that the library is only about 2 KiB when minified and gzipped, so the benefit from keeping it out of your bundle might be insignificant.

[exposify]: https://github.com/thlorenz/exposify
[jsDelivr-pkg]: https://www.jsdelivr.com/package/npm/backbone-fractal
[unpkg-pkg]: https://unpkg.com/backbone-fractal/


## Comparison with Marionette

[Marionette][marionette] offers a way to compose views out of smaller ones, too. This library already existed before backbone-fractal. So why did I create backbone-fractal, and why would you use it instead of Marionette? To answer these questions, let’s compare these libraries side by side.

feature | Marionette | backbone-fractal
---|---|---
regions/selectors | regions must be declared in advance | selector can be arbitrarily chosen
child views per region/selector | one | as many as you want
positioning of subviews | inside the region (region can contain nothing else) | before, inside or after the element identified by the selector
ordering of subviews | not applicable | free to choose
insertion of subview | manual, need to name a region | happens automatically at the right time inside the `render` method (exceptions possible when needed)
`CollectionView` class | keeps the subviews in sync with its collection | keeps the subviews in sync with its collection
`CollectionView.render` | destroys and recreates all of the subviews, even if its collection did not change | redraws only the HTML that doesn't belong to the subviews
other features | `emptyView`, `Behavior`, `MnObject`, `Application`, `ui`, `triggers`, bubbling events and then some | none
size, minified and gzipped | 9.3 KiB | 2.0 KiB

Overall, Marionette is a mature library that offers many features besides composing views. If you like those other features, then Marionette is for you.

On the other hand, backbone-fractal is small. It does only one thing, and it does it in a way that is more flexible and requires less code than Marionette. If all you need is an easy, modular and efficient way to compose view, you may be better off with backbone-fractal.

[marionette]: https://marionettejs.com


## Reference

- [Common interface](#common-interface)
    - [renderContainer](#rendercontainer)
    - [beforeRender, afterRender](#beforerender-afterrender)
    - [render](#render)
    - [remove](#remove)
- [CompositeView](#compositeview)
    - [constructor/initialize](#constructor-initialize)
    - [defaultPlacement](#defaultplacement)
    - [subviews](#subviews)
    - [forEachSubview](#foreachsubview)
    - [placeSubviews](#placesubviews)
    - [detachSubviews](#detachsubviews)
    - [removeSubviews](#removesubviews)
- [CollectionView](#collectionview)
    - [container](#container)
    - [subview](#subview)
    - [makeItem](#makeitem)
    - [initItems](#inititems)
    - [items](#items)
    - [initCollectionEvents](#initcollectionevents)
    - [insertItem](#insertitem)
    - [removeItem](#removeitem)
    - [sortItems](#sortitems)
    - [placeItems](#placeitems)
    - [resetItems](#resetitems)
    - [clearItems](#clearitems)
    - [detachItems](#detachitems)


### Common interface

Both [`CompositeView`](#compositeview) and [`CollectionView`](#collectionview) extend [Backbone.View][backbone-view]. The extensions listed in this section are common to both classes.

[backbone-view]: https://backbonejs.org/#View


#### renderContainer

```js
view.renderContainer() => view
```

*Default: no-op.*

Renders the *container* of the subviews. You should not *call* this method directly; call [`render`](#render) instead.

*Override* this method to render the HTML context in which the subviews will be placed, if applicable. In other words, mentally remove all subviews from your desired end result and if any internal HTML structure remains after this, render that structure in `renderContainer`. The logic is like the [`render`][backbone-render] method of a simple (i.e., non-composed) view.

[backbone-render]: https://backbonejs.org/#View-render

You *don’t* need to define `renderContainer` if your desired end result looks like this:

```html
<root-element>
    <sub-view-1></sub-view-1>
    <sub-view-2></sub-view-2>
</root-element>
```

You *do* need to define `renderContainer` if your desired end result looks like this:

```html
<root-element>
    <p>Own content</p>
    <div>
        <sub-view-1></sub-view-1>
        <sub-view-2></sub-view-2>
        <button>Also own content</button>
    </div>
</root-element>
```

In the latter case, your `renderContainer` method should do something that is functionally equivalent to the following:

```js
class Example extends CompositeView {
    renderContainer() {
        this.$el.html(`
            <p>Own content</p>
            <div>
                <button>Also own content</button>
            </div>
        `);
        return this;
    }
}
```

Of course, you are encouraged to follow the convention of using a [`template`][backbone-template] for this purpose.

[backbone-template]: https://backbonejs.org/#View-template

The `renderContainer` method is also a good place to define behaviour that should only affect the container HTML without touching the HTML of any of the subviews. For example, you may want to apply a jQuery plugin directly after setting `this.$el.html`.


#### beforeRender, afterRender

```js
view.beforeRender() => view
view.afterRender() => view
```

*Default: no-op.*

You can override these methods to add additional behaviour directly before or directly after rendering, respectively. Such additional behaviour could include, for example, administrative operations or triggering events.

```js
class Example extends CollectionView {
    beforeRender() {
        return this.trigger('render:start');
    }
    afterRender() {
        return this.trigger('render:end');
    }
}
```


#### render

```js
view.render() => view
```

`CompositeView` and `CollectionView` can (and should be) [rendered][backbone-render] just like any other `Backbone.View`. The `render` method is predefined to take the following steps:

1. Call `this.beforeRender()`.
2. Detach the subviews from `this.el`.
3. Call `this.renderContainer()`.
4. (Re)insert the subviews within `this.el`.
5. Call `this.afterRender()`.
6. Return `this`.

The predefined `render` method is safe and efficient. It is safe because it prevents accidental corruption of the subviews and enforces that each selector is matched at most once so no accidental “ghost copies” of subviews are made. It is efficient because it does not re-render the subviews; instead, it assumes that each individual subview has its own logic and event handlers to determine when it should render (we do, however, have a [recipe](#rendering-subviews-automatically-with-the-parent) for those who want to couple subview rendering to parent rendering).

You should never need to override `render`. For customizations, use the [`renderContainer`](#rendercontainer), [`beforeRender` and `afterRender`](#beforerender-afterrender) hooks instead.

The implementation of steps 2 and 4 differs between [`CompositeView`](#compositeview) and [`CollectionView`](#collectionview), though in both cases, you don’t need to implement them yourself. For details, refer to the respective sections of the reference.

`render` and [`remove`](#remove) have the special property that subviews are always detached in the opposite order in which they were inserted, even when you have a deeply nested hierarchy of subviews and sub-subviews or when you follow our recipe for [chimera views](#chimera-views).


#### remove

```js
view.remove() => view
```

Recursively calls `.remove()` on all subviews and finally cleans up `view`. Like with all Backbone views, *call* this method when you are done with the view. Like [`render`](#render), you should not need to *override* this method.

As mentioned in [`render`](#render), subviews are [removed][backbone-remove] in the opposite order in which they were inserted.

[backbone-remove]: https://backbonejs.org/#View-remove


### CompositeView

`CompositeView` lets you insert a heterogeneous set of subviews in arbitrary places within the HTML skeleton of your parent view. Its rendering logic is the same as that of [`CollectionView`](#collectionview), as described in the [common interface](#common-interface). This section describes how to specify the subviews, as well some utility methods.


#### constructor/initialize

```js
new DerivedCompositeView(options);
```

While these methods are the same as in [`Backbone.View`][backbone-view], it is recommended that you create the subviews here and keep them on `this` throughout the lifetime of the `CompositeView`. In some cases, you may also want to render a subview directly on creation. I suggest that you make each subview responsible for re-rendering itself afterwards whenever needed.

The following example class will be reused in the remainder of the `CompositeView` reference.

```js
import { BadgeView, DropdownView, ImageView } from '../your/own/code';

class Example extends CompositeView {
    initialize(options) {
        this.badge = new BadgeView(...);
        this.dropdown = new DropdownView(...);
        this.image = new ImageView(...);
        this.image.render();
    }
}
```


#### defaultPlacement

```js
Example.prototype.defaultPlacement: InsertionMethod
```

`InsertionMethod` can be one of [`'append'`][jquery-append], [`'prepend'`][jquery-prepend], [`'after'`][jquery-after], [`'before'`][jquery-before] or [`'replaceWith'`][jquery-replaceWith], roughly from most to least recommended.

*Default: `'append'`.*

This property determines how each subview will be inserted relative to a given element. You can override this for specific subviews on a case-by-case basis. Examples are provided next under [`subviews`](#subviews).

[jquery-append]: https://api.jquery.com/append/
[jquery-prepend]: https://api.jquery.com/prepend/
[jquery-after]: https://api.jquery.com/after/
[jquery-before]: https://api.jquery.com/before/
[jquery-replaceWith]: https://api.jquery.com/replaceWith/


#### subviews

```js
view.subviews: SubviewDescription[]
view.subviews() => SubviewDescription[]

SubviewDescription: {
    view: View,
    method?: InsertionMethod,
    selector?: string,
    place?: boolean
}
```

*Default: `[]`.*

The `subviews` property or method is the core administration with which you declare your subviews and which enables the `CompositeView` logic to work.

It is very important that each “live” subview appears **exactly once** in `subviews`. “Live” here means that the subview has been `new`ed and not (yet) manually `.remove()`d; it does not matter whether it is or should be in the DOM. If you want to (temporarily) skip a subview during insertion, do not omit it from `subviews`; use `place: false` instead. You may also set the `place` field to a function that returns a boolean, if the decision whether to insert a particular subviews depends on circumstances.

There is a lot of freedom in the way you can define the `subviews` array. Instead of a full-blown `SubviewDescription`, you may also just put a subview directly in the array, in which case default values are assumed for the `method`, `selector` and `place` fields. Each piece of information can be provided either directly or as a function that returns a value of the appropriate type. Functions will be bound to `this`. In addition, all pieces except for the `method` and `selector` fields may be replaced by a string that names a property or method of your view. So the following classes are all equivalent:

```js
// property with direct view names
class Example1 extends Example {}
Example1.prototype.subviews = ['badge', 'dropdown', 'image'];

// property with SubviewDescriptions with view names
class Example2 extends Example {}
Example2.prototype.subviews = [
    { view: 'badge' },
    { view: 'dropdown' },
    { view: 'image' },
];

// method with direct views by value
class Example3 extends Example {
    subviews() {
        return [this.badge, this.dropdown, this.image];
    }
}

// mix with the name of a method that returns a SubviewDescription
class Example4 extends Example {
    getBadge() {
        return { view: this.badge };
    }
    subviews() {
        return ['getBadge', 'dropdown', this.image];
    }
}
```

In most cases, setting `subviews` as a static array on the prototype should suffice. You only need to define `subviews` as a method if you are doing something fancy that causes the set of subviews to change during the lifetime of the parent view.

If you pass a `selector`, it should match exactly one element within the HTML skeleton of the parent view (i.e., the container of the subviews). If the `selector` does not match any element, the subview will simply not be inserted. As a precaution, if the `selector` matches more than one element, only the first matching element will be used. If you do not pass a selector, the root element of the parent view is used instead. We call the element that ends up being used the *reference element*.

The `method` determines how the subview is inserted relative to the reference element. If the `selector` is `undefined` (i.e., the reference element is the parent view’s root element), the `method` **must** be `'append'` or `'prepend'`, because the other methods work on the outside of the reference element. If not provided, the `method` defaults to [`this.defaultPlacement`](#defaultplacement), which in turn defaults to `'append'`. A summary of the available methods:

- [`'append'`][jquery-append]: make the subview the last child of the reference element.
- [`'prepend'`][jquery-prepend]: make the subview the first child of the reference element.
- [`'after'`][jquery-after]: make the subview the first sibling after the reference element.
- [`'before'`][jquery-before]: make the subview the last sibling before the reference element.
- [`'replaceWith'`][jquery-replaceWith]: (**danger!**) remove the reference element and put the subview in its place. I recommend using this only if you want to work with [custom elements](#custom-elements).

For the following examples, assume that [`example.renderContainer`](#rendercontainer) produces the following container HTML:

```html
<root-element>
    <p>Own content</p>
    <div>
        <button>Also own content</button>
    </div>
</root-element>
```

Continuing the definition of the `Example` class from before, the following

```js
class Example extends CompositeView {
    // initialize with badge, dropdown and image as before
    // renderContainer as above
}

Example.prototype.subviews = ['badge', 'dropdown', 'image'];

let example = new Example();
example.render();
```

gives

```html
<root-element>
    <p>Own content</p>
    <div>
        <button>Also own content</button>
    </div>
    <badge-view></badge-view>
    <dropdown-view></dropdown-view>
    <image-view></image-view>
</root-element>
```

From

```js
Example.prototype.subviews = ['badge', 'dropdown', 'image'];
Example.prototype.defaultPlacement = 'prepend';
```

we get

```html
<root-element>
    <image-view></image-view>
    <dropdown-view></dropdown-view>
    <badge-view></badge-view>
    <p>Own content</p>
    <div>
        <button>Also own content</button>
    </div>
</root-element>
```

From

```js
Example.prototype.subviews = [{
    view: 'badge',
    selector: 'button',
    place: false,
}, {
    view: 'dropdown',
    method: 'before',
    selector: 'button',
}, {
    view: 'image',
    method: 'prepend',
}];
```

we get

```html
<root-element>
    <image-view></image-view>
    <p>Own content</p>
    <div>
        <dropdown-view></dropdown-view>
        <button>Also own content</button>
    </div>
</root-element>
```

Finally, from

```js
class Example extends CompositeView {
    // ...
    shouldInsertBadge() {
        return this.subviews.length === 3;
    }
}

Example.prototype.subviews = [{
    view: 'badge',
    selector: 'button',
    place: 'shouldInsertBadge',
}, {
    view: 'dropdown',
    method: 'prepend',
    selector: 'div',
}, {
    view: 'image',
    method: 'after',
    selector: 'p',
}];
```

we get

```html
<root-element>
    <p>Own content</p>
    <image-view></image-view>
    <div>
        <dropdown-view></dropdown-view>
        <button>Also own content<badge-view></badge-view></button>
    </div>
</root-element>
```


#### forEachSubview

```js
view.forEachSubview(iteratee, [options]) => view

iteratee: (subview, referenceElement, method) => <ignored>
options: {
    reverse: boolean,
    placeOnly: boolean
}
```

This utility method processes [`view.subviews`](#subviews). It calls `iteratee` once for each entry, passing the subview, reference element and method as separate arguments. Defaults are applied, names are dereferenced and functions are invoked in order to arrive at the concrete values before invoking `iteratee`. It passes `view.$el` as the reference element if the subview description has no selector. In addition, `iteratee` is bound to `view` so that it can access `view` through `this`.

So if `view.subviews` evaluates to the following array,

```js
[
    'badge',
    {
        view: function() { return this.dropdown; },
        selector: 'button',
        method: 'before',
    },
]
```

then the expression `view.forEachSubview(iteratee)` will be functionally equivalent to the following sequence of statements:

```js
iteratee.call(view, view.badge,    view.$el,                 view.defaultPlacement);
iteratee.call(view, view.dropdown, view.$('button').first(), 'before');
```

If, for example, you want to emit an event from each subview reporting where it will be inserted, the following will work regardless of how you specified `view.subviews`:

```js
view.forEachSubview(function(subview, referenceElement, method) {
    subview.trigger('renderInfo', referenceElement, method);
});
```

If you pass `{placeOnly: true}`, all subviews for which the `place` option is explicitly set to (something that evaluates to) `false` are skipped. For example, if `view.subviews` has the following *three* entries,

```js
[
    'badge',
    'dropdown',
    {
        view: 'image',
        place: false,
    },
]
```

then the expression `view.forEachSubview(iteratee, {placeOnly: true})` will be functionally equivalent to the following *two* statements:

```js
iteratee.call(view, view.badge,    view.$el, view.defaultPlacement);
iteratee.call(view, view.dropdown, view.$el, view.defaultPlacement);
```

You may also pass `{reverse: true}` to process the subviews in reverse order. So with the following `view.subviews`,

```js
['badge', 'dropdown', 'image']
```

`subview.forEachSubview(iteratee, {reverse: true})` is equivalent to the following sequence of statements (note that `view.image` comes first and `view.badge` last):

```js
iteratee.call(view, view.image,    view.$el, view.defaultPlacement);
iteratee.call(view, view.dropdown, view.$el, view.defaultPlacement);
iteratee.call(view, view.badge,    view.$el, view.defaultPlacement);
```

#### placeSubviews

```js
view.placeSubviews() => view
```

This method puts all subviews that should be placed (as indicated by the `place` option in each subview description) in their target position within the container HTML. The predefined [`render`](#render) method calls `this.placeSubviews` internally. In general, you shouldn't call this method yourself; use [`render`](#render) instead.

There is, however, a corner case in which it does make sense to call `placeSubviews` directly: when you have non-trivial container HTML that you want to leave unchanged but you want to move the subviews to different positions within it. If you want to implement such behaviour, you should implement [`subviews`](#subviews) as a method so that it can return a different position for each subview, and possibly different insertion orders, depending on circumstances.

```js
class OrderedComposite extends CompositeView {
    initialize() {
        this.first = new View();
        this.first.$el.html('first');
        this.second = new View();
        this.second.$el.html('second');
    }
    subviews() {
        if (this.order === 'rtl') {
            return ['second', 'first'];
        } else {
            return ['first', 'second'];
        }
    }
    renderContainer() {
        // imagine this method generates a huge chunk of HTML
    }
}

let ordered = new OrderedComposite();
ordered.render();
// ordered.$el now contains a huge chunk of HTML, ending in
// <div>first</div><div>second</div>

ordered.order = 'rtl';
ordered.placeSubviews();
// still the same huge chunk of HTML, but now ending in
// <div>second</div><div>first</div>
```

It is wise to always first call [`view.detachSubviews()`](#detachsubviews) before manually calling `view.placeSubviews()`. In this way, you ensure that one subview cannot be accidentally inserted inside another subview if the latter subview happens to match your selector first.


#### detachSubviews

```js
view.detachSubviews() => view
```

This method takes all subviews out of the container HTML by calling `subview.$el.detach()` on each, in reverse order of insertion. The purpose of this method is to remove subviews *temporarily* so they can be re-inserted again later; all event listeners associated with the subviews stay active. This can be done to reset the parent to a pristine state with no inserted subviews, or before DOM manipulations in order to prevent accidental corruption of the subviews.

The predefined [`render`](#render) method calls `this.detachSubviews` internally and most of the time, you don't need to invoke it yourself. It is, however, entirely safe to do so at any time. You might do this if you're going to apply a jQuery plugin that might unintentionally affect your subviews, or if you're going to omit some of the subviews that used to be inserted. Usage of `detachSubviews` should generally follow the following pattern:

```js
class YourCompositeView extends CompositeView {
    aSpecialMethod() {
        this.detachSubviews();
        // apply dangerous operations to the DOM and/or
        // apply changes that cause {place: false} on some of the subviews
        this.placeSubviews();
        // probably return something
    }
}
```


#### removeSubviews

```js
view.removeSubviews() => view
```

*This is an irreversible operation. The reversible variant is [`detachSubviews`](#detachsubviews).*

This method takes all subviews out of the container HTML by calling `subview.remove()` on each, in reverse order of insertion. The purpose of this method is to remove subviews *permanently* and to unregister all their associated event listeners. This is facilitates garbage collection when the subviews aren't needed anymore.

The predefined [`remove`](#remove) method calls `this.removeSubviews` internally. The only reason to invoke it yourself would be to completely replace all subviews with a new set, or to clean up the subviews ahead of time, for example if you intend to continue using the parent as if it were a regular non-composite view.


### CollectionView

`CollectionView`, as the name suggests, lets you represent a [`Backbone.Collection`][backbone-collection] as a composed view in which each of the models is represented by a separate subview. Contrary to [`CompositeView`](#compositeview), the subviews are always kept together in the DOM, but the number of subviews is variable. It can automatically keep the subviews in sync with the contents of the collection. Its rendering logic is the same as that of `CompositeView`, as described in the [common interface](#common-interface). This section describes how to specify the subviews, as well some utility methods.

[backbone-collection]: https://backbonejs.org/#Collection


#### container

```js
view.container: string
```

The `container` property can be set to a jQuery selector to identify the element within your container HTML where the subviews should be inserted. If set, it is important that the selector identifies exactly one element within the parent view. If you leave this property `undefined`, the parent view’s root element will be used instead.

For some examples, suppose that [`view.renderContainer`](#rendercontainer) produces the following HTML.

```html
<root-element>
    <h2>The title</h2>
    <section class="listing">
        <!-- position 1 -->
    </section>
    <!-- position 2 -->
</root-element>
```

If you set `view.container = '.listing'`, the subviews will be appended after the `<!-- position 1 -->` comment node.

If, on the other hand, you don’t assign any value to `view.container`, then the subviews will be appended after the `<!-- position 2 -->` comment node.

If you want to use different `container` values during the lifetime of your view, an appropriate place to update it is inside the [`renderContainer`](#rendercontainer) method.


#### subview

```js
new view.subview([options]) => subview
```

The `subview` property should be set to the constructor function of the subview type. Example:

```js
class SubView extends Backbone.View { }

class ParentView extends CollectionView {
    initialize() {
        this.initItems();
    }
}

ParentView.prototype.subview = SubView;

let exampleCollection = new Backbone.Collection([{}, {}, {}]);
let parent = new ParentView({collection: exampleCollection});
parent.render();
// The root element of parent now holds three instances of SubView.
```

If you want to represent the models in the collection with a mixture of different subview types, this is also possible. Simply override the [`makeItem`](#makeitem) method to return different subview types depending on the criteria of your choice. It is up to you whether and how to use the `subview` property in that case.


#### makeItem

```js
view.makeItem([model]) => subview
```

This method is invoked whenever a new subview must be created for a given `model`. The default implementation is equivalent to `new view.subview({model})`. If you wish to pass additional options to the [subview constructor](#subview) or to bind event handlers on the newly created `subview`, override the `makeItem` method. You are allowed to return different view types as needed.

Normally, you do not need to invoke this method yourself.


#### initItems

```js
view.initItems() => view
```

This method initializes the [internal list of subviews](#items), invoking [`view.makeItem`](#makeitem) for each model in `view.collection`. You must invoke this method once in the constructor or the `initialize` method of your `CollectionView` subclass.

```js
class Example extends CollectionView {
    initialize() {
        this.initItems();
    }
}
```

If you wish to show only a subset of the collection, use a `Backbone.Collection` adapter that takes care of the subsetting. See our [pagination recipe](#pagination) for details.


#### items

```js
view.items: subview[]
```

This property holds all of the subviews in an array. It is created by [`view.initItems`](#inititems). You can iterate over this array if you need to do something with every subview.

```js
view.items.forEach(subview => subview.render());
// All subviews of view have now been rendered.

import { isEqual } from 'underscore';
const modelsFromSubviews = view.items.map(subview => subview.model);
const modelsFromCollection = view.collection.models;
isEqual(modelsFromSubviews, modelsFromCollection); // true
```

Do not manually change the contents or the order of the `items` array; but see the next section on the [`initCollectionEvents`](#initcollectionevents) method.


#### initCollectionEvents

```js
view.initCollectionEvents() => view
```

This method binds [event handlers][backbone-event-catalog] on `view.collection` to keep [`view.items`](#items) and the DOM in sync. In most cases, you should invoke this method together with [`initItems`](#inititems) in the constructor or the `initialize` method:

```js
class Example extends CollectionView {
    initialize() {
        this.initItems().initCollectionEvents();
    }
}
```

On this same line, you may as well invoke [`.render`](#render) for the first time:

```js
class Example extends CollectionView {
    initialize() {
        this.initItems().initCollectionEvents().render();
    }
}
```

Whether you want to do this is up to you. The order of these invocations does not matter, except that `initItems` should be invoked before `render`.

Rare reasons to *not* invoke `initCollectionEvents` may include the following:

- You expect the collection to never change.
- You expect the collection to change, but you want to create a “frozen” representation in the DOM that doesn’t follow changes in the collection.
- You expect the collection to change and you do want to update the DOM accordingly, but only to a limited extend or in a special way, or you want to postpone the updates until after certain conditions are met.

In the latter case, you may want to manually bind a subset of the event handlers or adjust the handlers themselves. The following sections provide further details on the event handlers.

[backbone-event-catalog]: https://backbonejs.org/#Events-catalog


#### insertItem

```js
view.insertItem(model, [collection, [options]]) => view
```

*Default handler for the [`'add'` event][backbone-event-catalog] on `view.collection`.*

This method calls [`view.makeItem(model)`](#makeitem) and inserts the result in [`view.items`](#items), trying hard to give it the same position as `model` in `collection`. For a 100% reliable way to ensure matching order, see [`sortItems`](#sortitems).


#### removeItem

```js
view.removeItem(model, collection, options) => view
```

*Default handler for the [`'remove'` event][backbone-event-catalog] on `view.collection`.*

This method takes the subview at `view.items[options.index]`, calls `.remove` on it and finally deletes it from [`view.items`](#items). If you invoke `removeItem` manually, make sure that `options.index` is the actual (former) index of `model` in `collection`.


#### sortItems

```js
view.sortItems() => view
```

*Default handler for the [`'sort'` event][backbone-event-catalog] on `view.collection`.*

This method puts [`view.items`](#items) in the exact same order as the corresponding models in `view.collection`. A precondition for this to work, is that the set of models represented in `view.items` is identical to the set of models in `view.collection`; under typical conditions, this is the job of [`insertItem`](#insertitem) and [`removeItem`](#removeitem).

If you expect the `'sort'` event to trigger very often, you can save some precious CPU cycles by [debouncing][underscore-debounce] `sortItems`:

```js
import { debounce } from 'underscore';

class Example extends CollectionView {
    // ...
}

Example.prototype.sortItems = debounce(CollectionView.prototype.sortItems, 50);
```

In the example, we debounce by 50 milliseconds, but you can of course choose a different interval.

[underscore-debounce]: https://underscorejs.org/#debounce


#### placeItems

```js
view.placeItems() => view
```

*Default handler for the [`'update'` event][backbone-event-catalog] on `view.collection`.*

This method appends all subviews in [`view.items`](#items) to the element identified by [`view.container`](#container) or directly to `view.$el` if [`view.container`](#container) is undefined. If the subviews were already present in the DOM, no copies are made, but the existing elements in the DOM are reordered to match the order in `view.items`. If `view.items` matches the order of `view.collection` (for example because of a prior call to [`view.sortItems`](#sortitems)), this effectively puts the subviews in the same order as the models in `view.collection`.

Like in [`view.sortItems`](#sortitems), you can [debounce][underscore-debounce] this method if you expect the `'update'` event to trigger often.


#### resetItems

```js
view.resetItems() => view
```

*Default handler for the [`'reset'` event][backbone-event-catalog] on `view.collection`.*

This method is equivalent to `view.clearItems().initItems().placeItems()` (see [`clearItems`](#clearitems), [`initItems`](#inititems), [`placeItems`](#placeitems)). It removes and destroys all existing subviews, creates a completely fresh set matching `view.collection` and inserts the new subviews in the DOM.

Like in [`view.sortItems`](#sortitems), you can [debounce][underscore-debounce] this method if you expect the `'reset'` event to trigger often.


#### clearItems

```js
view.clearItems() => view
```

This method calls `.remove` on each subview in [`view.items`](#items). Generally, you won’t need to invoke `clearItems` yourself; it is called internally in [`remove`](#remove) and in [`resetItems`](#resetitems).


#### detachItems

```js
view.detachItems() => view
```

This method takes the subviews in [`view.items`](#items) temporarily out of the DOM in order to protect their integrity. It is called internally by the [`render`](#render) method. Calling this method is perfectly safe, although it is unlikely that you will need to do so.


## Recipes

*Need a recipe for your own use case? Drop me an [issue][issue-list]!*

[issue-list]: https://gitlab.com/jgonggrijp/backbone-fractal/issues/


### Pagination

> While this recipe describes how to do pagination with a `CollectionView`, it illustrates a more general principle. *Whenever you want to show only a subset of a collection in a `CollectionView`, this is best achieved by employing an intermediate adapter collection which contains only the subset in question.*

Suppose you have a collection, `library`, which contains about 1000 models of type `Book`. You want to show the `library` in a `CollectionView`, but you want to show only 20 books at a time, providing “next” and “previous” buttons so the user can browse through the collection. This is quite easy to achieve using [backbone.paginator][backbone-paginator] and a regular `CollectionView`.

[backbone-paginator]: https://github.com/backbone-paginator/backbone.paginator

backbone.paginator provides the `PageableCollection` class, which can hold a single page of some underlying collection and which provides methods for selecting different pages. When you select a different page, the models in that page replace the contents of the `PageableCollection`. The class has three *modes*, “server”, “client” and “infinite”, which respectively let you fetch and hold one page at a time, fetch and hold all data at once or fetch the data one page at a time and hold on to all data already fetched. In the “client” and “infinite” modes, you can access the underlying collection, i.e., the one containing all of the models that were already fetched, as the `fullCollection` property.

In the following example, we use client mode because this is probably most similar to a situation without `PageableCollection`. However, of course you can use a different mode and different settings; the recipe remains roughly the same. Suppose that your `library` collection would otherwise look like this:

```js
import { Collection } from 'backbone';

class Books extends Collection { }
Books.prototype.url = '/api/books';

let library = new Books();
// Get all books from the server.
library.fetch();
// Will trigger the 'update' event when fetching is ready.
```

then we can change it into the following to fetch all 1000 books at once, but expose only the first 20 books initially:

```js
import { PageableCollection } from 'backbone.paginator';

class Books extends PageableCollection { }
Books.prototype.url = '/api/books';

let library = new Books(null, {
    mode: 'client',
    state: {
        pageSize: 20,
    },
});
// Get all books from the server.
library.fetch();
// When the 'update' event is triggered, library.models will contain
// the first 20 books, while library.fullCollection.models will
// contain ALL books.
```

Having adapted our `library` thus, presenting one page at a time with a `CollectionView` is now almost trivial:

```js
import { BookView } from '../your/own/code';

// Example static template with a reserved place for the books and
// some buttons. With a real templating engine like Underscore or
// Mustache, you could make this more sophisticated, for example by
// hiding buttons that are not applicable or by showing the total and
// current page numbers.
const libraryTemplate = `
<h1>My Books</h1>
<table>
    <thead>
        <tr><th>Title</th><th>Author</th><th>Year</th></tr>
    </thead>
    <tbody>
        <!-- book views will be inserted here -->
    </tbody>
</table>
<footer>
    <button class="first-page">first</button>
    <button class="previous-page">previous</button>
    <button class="next-page">next</button>
    <button class="last-page">last</button>
</footer>
`;

class LibraryView extends CollectionView {
    initialize() {
        this.initItems().initCollectionEvents();
    }
    renderContainer() {
        this.$el.html(this.template);
        return this;
    }

    // Methods for browsing to a different page.
    showFirst() {
        this.collection.getFirstPage();
        return this;
    }
    showPrevious() {
        this.collection.getPreviousPage();
        return this;
    }
    showNext() {
        this.collection.getNextPage();
        return this;
    }
    showLast() {
        this.collection.getLastPage();
        return this;
    }
}

LibraryView.prototype.template = libraryTemplate;
LibraryView.prototype.subview = BookView;
LibraryView.prototype.container = 'tbody';
LibraryView.prototype.events = {
    'click .first-page': 'showFirst',
    'click .previous-page': 'showPrevious',
    'click .next-page': 'showNext',
    'click .last-page': 'showLast',
};

// That's all! Just use it like a regular view.
let libraryView = new LibraryView({ collection: library });
library.render().$el.appendTo(document.body);
// As soon as library is done fetching, the user will see the first
// page of 20 books. If she clicks on the "next" button, the next
// page will appear, etcetera.
```


### Mixins

Suppose you want to make a subclass of `CompositeView`. However, you want this same subclass to also derive from `AnimatedView`, a class provided by some other library. Both `CompositeView` and `AnimatedView` derive directly from `Backbone.View` and given JavaScript’s single-inheritance prototype chain, there is no obvious way in which you can extend both at the same time. Fortunately, Backbone’s `extend` method lets you easily mix one class into another:

```js
import { CompositeView } from 'backbone-fractal';
import { AnimatedView } from 'some-other-library';

export const AnimatedCompositeView = AnimatedView.extend(
    CompositeView.prototype,
    CompositeView,
);
```

Neither this problem nor its solution is specific to backbone-fractal, but there you have it.

If you are using TypeScript, there is one catch. The [`@types/backbone`][backbone-types] package currently has incomplete typings for the `extend` method, which renders the TypeScript compiler unable to infer the correct type for `AnimatedCompositeView`. Until this problem is fixed, you can work around it by making a few special type annotations:

```ts
import { ViewOptions } from 'backbone';  // defined in @types/backbone
import { CompositeView } from 'backbone-fractal';
import { AnimatedView } from 'some-other-library';

export type AnimatedCompositeView = CompositeView & AnimatedView;
export type AnimatedCompositeViewCtor = {
    new(options?: ViewOptions): AnimatedCompositeView;
} & typeof CompositeView & typeof AnimatedView;

export const AnimatedCompositeView = AnimatedView.extend(
    CompositeView.prototype,
    CompositeView,
) as AnimatedCompositeViewCtor;
```

[backbone-types]: https://www.npmjs.com/package/@types/backbone


### Rendering subviews automatically with the parent

Rendering the subviews automatically when the parent view renders is generally **not recommended** because this negates one of the key benefits of backbone-fractal: the ability to selectively update the parent view without having to re-render all of the subviews. It is much more efficient to have each view in a complex hierarchy take care of refreshing itself while leaving all other views unchanged, *including its subviews*, than to always refresh an entire hierarchy. Image re-rendering a big table just because the caption changed, or re-rendering a big modal form just because the status message in its title bar changed; it is a waste of energy and time, potentially causing noticeable delays for the user as well.

With this out of the way, I realise that I cannot foresee all possible use cases. There may be corner cases where there truly is a valid reason for always re-rendering the subviews when the parent renders. Doing this is quite straightforward.

It takes just one line of code, which should be added to the [`beforeRender`](#beforerender), [`renderContainer`](#rendercontainer) or [`afterRender`](#afterrender) hook of the parent view. It does not really matter which hook you choose; the end effect is the same except for the timing.

If the parent view is a [`CompositeView`](#compositeview), add the following line:

```js
this.forEachSubview(sv => sv.render(), { placeOnly: true });
```

If the parent view is a [`CollectionView`](#collectionview), add the following line instead:

```js
this.items.forEach(sv => sv.render());
```

Needless to say, the automatic re-rendering of subviews does not “bleed through” to lower levels of the hierarchy. If a subview is itself the parent of yet smaller subviews, these sub-subviews will not automatically re-render as a side effect. If you want to make them automatically re-render as well, add the same line to intermediate parent views.


### Custom elements

It is currently fashionable in other frameworks, such as [React][react], [Angular][angular] and [Vue][vue], to represent subviews (invariably called *components*) as custom elements in the template of the parent view. A similar approach is also taken in the upcoming [Web Components][web-components] standard. This is approximately the same notation we have seen so far in our pseudo-HTML examples, except that it is literally what is written in the template code (or in the case of Web Components, directly in plain HTML):

```html
<p>Some text</p>
<div>
    <sub-view-1></sub-view-1>
    <sub-view-2></sub-view-2>
    <button>Click me</button>
</div>
```

[react]: https://reactjs.org
[angular]: https://angular.io
[vue]: https://vuejs.org
[web-components]: https://www.webcomponents.org

Backbone and backbone-fractal are fit for implementing true Web Components and conversely, Web Components can be integrated in any templating engine. However, Web Components is not 100% ready for production. If you like the pattern, you can also mimic it to some extent with `CompositeView` by employing the `'replaceWith'` [insertion method](#defaultplacement). For the following example code, we will reuse the [`Example` class](#constructor-initialize) from the [`CompositeView` reference](#compositeview), repeated below:

```js
import { BadgeView, DropdownView, ImageView } from '../your/own/code';

class Example extends CompositeView {
    initialize(options) {
        this.badge = new BadgeView(...);
        this.dropdown = new DropdownView(...);
        this.image = new ImageView(...);
        this.image.render();
    }
}
```

In the most basic case, you just insert custom elements in your template in the places where the subviews should appear, use these custom elements as selectors in [`subviews`](#subviews) and set [`defaultPlacement`](#defaultplacement) to `'replaceWith'`:

```js
class Example extends CompositeView {
    // ...
    renderContainer() {
        this.$el.html(this.template);
        return this;
    }
}

Example.prototype.template = `
<p>Some text</p>
<image-view></image-view>
<div>
    <dropdown-view></dropdown-view>
    <button>Click me<badge-view></badge-view></button>
</div>
`;
Example.prototype.defaultPlacement = 'replaceWith';
Example.prototype.subviews = [{
    view: 'badge',
    selector: 'badge-view',
}, {
    view: 'dropdown',
    selector: 'dropdown-view',
}, {
    view: 'image',
    selector: 'image-view',
}];
```

Perhaps you want to take this one step further. You might want the custom element names to be intrinsic to each view class. For example, you may want a `BadgeView` to *always* appear as `badge-view` in your templates. If this is the case, you probably don’t want to have to repeat that name as the `selector` in the subview description every time. You may also want to *keep* the same custom element name in the final HTML.

`Backbone.View`’s [`tagName`][backbone-extend] property is the ideal place to document a fixed custom element name for a view class. We can have all of the above by using `tagName` and by doing some runtime preprocessing of the `subviews` array. If you take this route, however, I recommend that you start all custom element names in your application with a common prefix. For example, if your application is called *Awesome Webapplication*, you could start all custom element names with `aw-`, so the `tagName` of `BadgeView` would become `aw-badge` instead of `badge-view`.

[backbone-extend]: https://backbonejs.org/#View-extend

With that out of the way, it is time to show an oversimplified version of the code that you need to realise intrinsic custom elements. Assume that `BadgeView`, `DropdownView` and `ImageView` already have their `tagName`s set to `aw-badge`, `aw-dropdown` and `aw-image`, respectively:

```js
import { result, isString } from 'underscore';

// The preprocessing function where most of the magic happens.
// It processes one subview description at a time.
function preprocessSubviewDescription(description) {
    let view, place;
    // Note that we don't support custom selector or method, yet.
    if (isString(description)) {
        view = description;
    } else {
        { view, place } = description;
    }
    if (isString(view)) view = result(this, view);
    let selector = view.tagName;
    return { view, selector, place };
}

class Example extends CompositeView {
    // ...

    // Note that subviews is now a dynamic function instead of a static array.
    subviews() {
        return this._subviews.map(preprocessSubviewDescription.bind(this));
    }
}

Example.prototype.template = `
<p>Some text</p>
<aw-image></aw-image>
<div>
    <aw-dropdown></aw-dropdown>
    <button>Click me<aw-badge></aw-badge></button>
</div>
`;
Example.prototype.defaultPlacement = 'replaceWith';
// _subviews is the un-preprocessed precursor to subviews.
Example.prototype._subviews = ['badge', 'dropdown', 'image'];
// Note the leading '_' and the fact that we don't include selectors anymore.
// For simplicity of the preprocessSubviewDescription function, we restrict
// ourselves to either just the name of a subview or a description containing
// the name of a subview, in the latter case with an optional place field.
```

The example code above works, but there are some caveats. Most importantly, the preprocessing function doesn’t support customized selectors. This is going to be a problem as soon as you have two subviews of the same class within the same parent, because they will share the same selector. We could fix this by amending the `preprocessSubviewDescription` function so that it also accepts optional `selectorPrefix` and `selectorSuffix` fields:

```js
function preprocessSubviewDescription(description) {
    let view, place, selectorPrefix, selectorSuffix;
    if (isString(description)) {
        view = description;
    } else {
        { view, place, selectorPrefix, selectorSuffix } = description;
    }
    if (isString(view)) view = result(this, view);
    let prefix = selectorPrefix || '';
    let suffix = selectorSuffix || '';
    let selector = prefix + view.tagName + suffix;
    return { view, selector, place };
}

// Now, let's adapt our template a bit to demonstrate how the above
// modifications solve our problem when we have two badge subviews.

Example.prototype.template = `
<p>Some text<aw-badge></aw-badge></p>
<aw-image></aw-image>
<div>
    <aw-dropdown></aw-dropdown>
    <button>Click me<aw-badge></aw-badge></button>
</div>
`;

// Note how we use prefixes to distinguish the badges.
Example.prototype._subviews = [
    {
        view: 'introBadge',
        selectorPrefix: 'p ',
    },
    {
        view: 'buttonBadge',
        selectorPrefix: 'button ',
    },
    'dropdown',
    'image',
];
```

This last example would be safe to use in production. The final HTML output by the [`render`](#render) method would be identical to the template, except that the instances of `<aw-badge>`, `<aw-dropdown>` and `<aw-image>` would have their own internal structure.

You could go even further. You might, for example, further extend `preprocessSubviewDescription` to permit exceptions to the rule that all subviews are inserted through the `replaceWith` method. These and other sophistications are however outside of the scope of this recipe.


### Chimera views

There are many situations where one might want to create a view that combines aspects from both [`CompositeView`](#compositeview) and [`CollectionView`](#collectionview). For example, imagine that you are implementing a sortable table view with a few fixed clickable column headers and a variable set of rows, one for each model in a collection. The structure of your table view might look as follows in pseudo-HTML:

```html
<table>
    <thead>
        <tr>
            <clickable-header-view></clickable-header-view>
            <clickable-header-view></clickable-header-view>
        </tr>
    </thead>
    <tbody>
        <row-view></row-view>
        <row-view></row-view>
        <row-view></row-view>
        <row-view></row-view>
        <!-- ... -->
    </tbody>
</table>
```

In nearly all cases, including this example, there will be an element in your structure that contains all of the variable subset of subviews and none of the fixed subset of subviews. In the example, that element is the `<tbody>`. You can always make that element a `CollectionView` in its own right and make that a subview of a `CompositeView` which also contains the fixed subviews. So in our example, the table as a whole becomes a `CompositeView` with the following structure:

```html
<table>
    <thead>
        <tr>
            <clickable-header-view></clickable-header-view>
            <clickable-header-view></clickable-header-view>
        </tr>
    </thead>
    <table-collection-view></table-collection-view>
</table>
```

and the `TableCollectionView` in turn is a `CollectionView` with the following structure.

```html
<tbody>
    <row-view></row-view>
    <row-view></row-view>
    <row-view></row-view>
    <row-view></row-view>
    <!-- ... -->
</tbody>
```

> Side remark: it is only a small step from here to make the `<thead>` a `CollectionView` as well, so you can support a variable number of columns.

In nearly all remaining cases (where there is no element available that you can turn into a `CollectionView` subview), you can restructure your HTML to end up in the same situation after all. For example, suppose that you started out with an old-fashioned “flat” table:

```html
<table>
    <tr>
        <clickable-header-view></clickable-header-view>
        <clickable-header-view></clickable-header-view>
    </tr>
    <row-view></row-view>
    <row-view></row-view>
    <row-view></row-view>
    <row-view></row-view>
    <!-- ... -->
</table>
```

then you can just add `<thead>` and `<tbody>` elements, creating the situation with which we started.

In rare cases, however, circumstances will force you to insert the variable subviews in the same element as the fixed subviews. One example of this is Bulma’s [panel class][bulma-panel], where you might want to have a couple of fixed `.panel-block`s or `.panel-tabs`s with controls at the top and bottom and a variable number of `.panel-block`s in between to represent some collection. [Bulma’s example][bulma-panel] could look like this in our pseudo-HTML notation:

```html
<panel-view class="panel">
    <p class="panel-heading">repositories</p>
    <search-view class="panel-block"></search-view>
    <tabs-view class="panel-tabs"></tabs-view>
    <repository-view class="panel-block"></repository-view>
    <repository-view class="panel-block"></repository-view>
    <repository-view class="panel-block"></repository-view>
    <!-- variable number of repository-views -->
    <button-view class="panel-block"></button-view>
</panel-view>
```

[bulma-panel]: https://bulma.io/documentation/components/panel/

Bulma requires all parts to be nested directly within the `.panel` element in order for its styling to work, so we can’t cleanly separate out the variable subset of the subviews.

For this type of situation, we need to create a *chimera view*. The principle is to create a `CompositeView` and a `CollectionView` that share the same root element, where one owns the other “off the record”, i.e., without treating it as a regular subview. We may refer to the owning view as the *master* and the other as the *slave*. The slave is hidden from the view client and does not alter the view’s HTML content. The master serves as the public interface and calls methods of the slave under the hood. It also implements the `.renderContainer` method if needed.

In most cases, it is probably most straightforward to make the `CollectionView` the master, because this makes it easiest to position the fixed subviews relative to the variable subviews. Our Bulma panel example then looks like this:

```js
import { CompositeView, CollectionView } from 'backbone-fractal';
import {
    SearchView,
    TabsView,
    ButtonView,
    RepositoryView,
    someCollection,
} from '../your/own/code';

class PanelSlave extends CompositeView {
    initialize() {
        this.searchView = new SearchView();
        this.tabsView = new TabsView();
        this.buttonView = new ButtonView();
    }
    // We don't need to define or override any other method in this
    // case.
}

// Note that we insert tabsView first and then searchView, because
// prepending multiple elements to the same parent element will make
// them appear in the reverse order of insertion.
PanelSlave.prototype.subviews = [{
    view: 'tabsView',
    method: 'prepend',
}, {
    view: 'searchView',
    method: 'prepend',
}, 'buttonView'];

// Panel is the master and represents the complete chimera view.
class Panel extends CollectionView {
    initialize() {
        this.initItems().initCollectionEvents();
        this.slave = new PanelSlave({el: this.el});
    }
    beforeRender() {
        this.slave.detachSubviews();
        return this;
    }
    renderContainer() {
        // this.template is left to your imagination. Its only
        // duty is to produce the p.heading, since all other
        // contents of the panel are subviews.
        this.$el.html(this.template({}));
        return this;
    }
    afterRender() {
        this.slave.placeSubviews();
        return this;
    }
    remove() {
        this.slave.remove();
        return super.remove();
    }
    setElement(el) {
        if (this.slave) this.slave.setElement(el);
        return super.setElement(el);
    }
}

Panel.prototype.subview = RepositoryView;

// To use, just treat Panel like any other view class.
let somePanel = new Panel({collection: someCollection});
// Draw the entire panel structure as in our pseudo-HTML structure.
somePanel.render().$el.appendTo(document.body);
// Clean up after use.
somePanel.remove();
```

In general, the pattern looks like this:

```js
class ChimeraSlave extends CompositeView {
    // Like any other CompositeView, with some simplifications.
    // Must NOT define .renderContainer.
    // Should not define .el, .id, .tagName, .className, .attributes.

    render() {
        // The slave's .render method should never be called. Calling
        // the inherited .render method will not seriously break
        // anything, but it will likely insert the slave's subviews
        // in the wrong order relative to the master's subviews.
        // For ultimate robustness, make it a no-op.
        return this;
    }

}

// master:
class Chimera extends CollectionView {
    // Like any other CollectionView, with the following additions.

    initialize() {
        // The following line may be placed in the constructor
        // instead. It does not matter whether it runs before or
        // after this.initItems.initCollectionEvents(), but it must
        // run before the first .render().
        this.slave = new ChimeraSlave({
            // The slave MUST have the same .el as the master.
            el: this.el,
            // If there is a model, the slave probably needs it, too.
            model: this.model,
            // You can forward other options as needed.
        });
    }

    beforeRender() {
        this.slave.beforeRender();
        // pre-render operations of the master
        this.slave.detachSubviews();
    }
    afterRender() {
        this.slave.placeSubviews();
        // post-render operations of the master
        this.slave.afterRender();
    }
    remove() {
        this.slave.remove();
        return super.remove();
    }
    setElement(el) {
        // Keeps the .el in sync. We check whether the slave exists,
        // because .setElement is always invoked once during
        // construction when the slave hasn't been created yet.
        if (this.slave) this.slave.setElement(el);
        return super.setElement(el);
    }
}

// In general, chimera views will often be passed both a model and a
// collection, since they combine aspects of both CompositeView and
// CollectionView.
let someChimera = new Chimera({ model: someModel, collection: someCollection });
```

Note that the slave’s rendering steps “wrap around” the master’s rendering steps in the same order as in the slave’s inherited [`.render`](#render) method. This preserves the property that all subviews are removed from the DOM in the opposite order in which they were inserted. The overridden `.remove` method preserves this property, too.

There are many variations possible. Depending on your needs, you may want to reverse roles between the `CompositeView` and the `CollectionView`, where the former becomes the master and the latter the slave. The pattern can also be extended to multiple slaves, for example if there are multiple collections in play.


[shield-pipeline]: https://gitlab.com/jgonggrijp/backbone-fractal/badges/develop/pipeline.svg
[shield-npm]: https://img.shields.io/npm/v/backbone-fractal
[shield-gitlab]: https://img.shields.io/badge/-GitLab-555?logo=gitlab
[shield-issues]: https://img.shields.io/badge/-issues-555?logo=gitlab
[shield-jgonggrijp]: https://img.shields.io/badge/author-Julian_Gonggrijp-708
[shield-license]: https://img.shields.io/npm/l/backbone-fractal
