documentcloud/backbone — pull request #1437 by RStankov (2012-06-21 18:25:14+00:00)
Title: Trigger lifetime view events on Backbone.View

I'm working on several ```Backbone.View``` extensions, currently. Some of them, I have [open-sourced](https://github.com/RStankov), some I may open-source in the future.

Currently if I want to have some behavior when view is removed, for example, I have three feasible options:

* store the current ```Backbone.View.prototype.remove```, replace it with my method, then call it from there to keep the remove behavior.
* ```MyCustomVIew``` extends ```Backbone.View``` and just replace the whole ```Backbone.View``` with ```MyCustomView```
* ```MyCustomVIew``` extends ```Backbone.View``` and when someone wants to use it, just extends ```MyCustomVIew``` instead of ```Backbone.View```

The last way, is nice if you don't have external Views, or you are not providing a external extensions. Because several people may want to have more than one extension in the same view. For example I want to have both ```Backbone.ViewWithSubviews``and ```Backbone.ViewWithOtherExtentions``` at the same time.

I think most of the plugins need to be able to run before ```Backbone.View#initialize``` and ```Backbone.View#remove```.
I was playing with possible plugin solutions, but I couldn't figure out an nice api for them.
So in the end, I just add lifetime events triggered directly to ```Backbone.View```.

For example:

```coffee
view = new CustomView() # triggers 'configure', 'initialize' on the Backbone.View object itself
view.setElement(otherElement) # triggers 'change:element' on the Backbone.View object itself
view.remove() # triggers 'remove' on the Backbone.View object itself

# so an user can
Backbone.View.on 'remove', (view) -> console.log "#{view.cid} was removed"
````

At least with my prototypes this worked perfectly and will give a lot more options for extending ```Backbone.View```.

Here are some minor examples:

My [Backbone.BindTo](https://github.com/RStankov/backbone-bind-to) can be rewritten to be just:

```coffee

Backbone.View.on 'configure', (view) ->
# this out of scope function for binding events
bindTo view, view.bindToModel, view.model if view.model
bindTo view, view.bindToCollection, view.collection if view.collection

Backbone.View.on 'remove', (view) ->
# this out of scope function for unbinding events
unbindFrom view, view.bindToModel, view.model if view.view
unbindFrom view, view.bindToCollection, view.collection if view.collection

```

*my current implementation replace ```Backbone.View``` with my view ```Backbone.BindTo.View```*

An other nice example is, having ```optionNames``` for a view:

```coffee
Backbone.View.on 'configure', (view, options) ->
for value, name in view.optionNames
view[name] = options[name] || value
delete options[name]

# this will enable, replacing this:
class MyView extends Backbone.View
initialize: (options) ->
@offset = options.offset || 1
@size = options.size || 2

# with that:
class MyView extends Backbone.View
defaultOptions:
offset: 1,
size: 2

```

Creating a plugin for sub-views (there are a lot of those) not to be like this:

```coffee
Backbone.ViewWithSubviews extends Backbone.View
addSubView: (view) ->
@subViews ?= []
@subViews.push view

remove:
_.invoke @subViews, 'remove'
super

Backbone.View = Backbone.ViewWithSubviews

# and just be
Backbone.View::addSubView: (view) ->
addSubView: (view) ->
@subViews ?= []
@subViews.push view

Backbone.View.on 'remove', (view) ->
_.invoke view.subViews, 'remove'
```

I was also wondering if is needed to trigger normal view events for ```change:element``` and ```remove```. If you want I can add those events to.