Skip to content

Commit

Permalink
add behaviors
Browse files Browse the repository at this point in the history
  • Loading branch information
samccone committed Mar 21, 2014
1 parent b8075a2 commit 8b09a28
Show file tree
Hide file tree
Showing 9 changed files with 773 additions and 1 deletion.
2 changes: 2 additions & 0 deletions Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ module.exports = function(grunt) {
'src/marionette.controller.js',
'src/marionette.domRefresh.js',
'src/marionette.view.js',
'src/marionette.behaviors.js',
'src/marionette.behavior.js',
'src/marionette.itemview.js',
'src/marionette.collectionview.js',
'src/marionette.compositeview.js',
Expand Down
197 changes: 197 additions & 0 deletions docs/marionette.behavior.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
# Marionette.Behavior


A `Behavior` is an isolated set of DOM / user interactions interactions that can be mixed into any `View`. `Behaviors` allow you to blackbox `View` specific interactions into portable logical chunks, keeping your `views` simple and your code DRY.

## Documentation Index

* [Motivation](#the-motivation)
* [Using Behaviors](#using)
* [API](#api)
* [Event proxy](#the-event-proxy)
* [$](#$)
* [$el](#$el)
* [Defaults](#defaults)
* [View](#view)

## The Motivation

As you build more and more complex views you will find that your `view` becomes less about displaying model data, and more about interactions.

These interactions tend to be chunks of logic that you want to use in multiple views.

## Using

Here is an example of a simple `itemView`. Let's take a stab at simplifying it, and abstracting behaviors from it.

```js
var MyView = Marionette.ItemView.extend({
ui: {
"close": ".close-btn"
},

events: {
"click @ui.close": "warnBeforeClose"
},

warnBeforeClose: function() {
alert("you are closing all your data is now gone!");
this.close();
},

onShow: function() {
this.$('.tooltip').tooltip({
text: "what a nice mouse you have"
});
}
});
```

Interaction points such as tooltips and warning messages are generic concepts. There is no need to recode them within your views. They are prime for abstraction into a higher level non-coupled concept, which is exactly what Behaviors provide you with.

Here is the syntax for declaring which behaviors get used within a view.
The keys in the hash are passed to `getBehaviorClass` to lookup the correct `Behavior` class.
The options for each behavior are also passed to said Behavior during initialization. The options are then stored within each behavior under `options`.

```js
var MyView = Marionette.ItemView.extend({
behaviors: {
CloseWarn: {
message: "you are closing all your data is now gone!"
},
ToolTip: {
text: "what a nice mouse you have"
}
}
});
```

Now let's create the `CloseWarn` behavior.

```js
var CloseWarn = Marionette.Behavior.extend({
// you can set default options
// just like you can in your Backbone Models
// they will be overriden if you pass in an option with the same key
defaults: {
"message": "you are closing!"
},

// behaviors have events that are bound to the views DOM
events: {
"click .close": "warnBeforeClose"
},

warnBeforeClose: function() {
alert(this.options.message);
// every Behavior has a hook into the
// view that it is attached to
this.view.close();
}
});
```

And onto the `Tooltip` behavior.

```js
var ToolTip = Marionette.Behavior.extend({
onShow: function() {
// this.$ is another example of something
// that is exposed to each behavior instance
// of the view
this.$('.tooltip').tooltip({
text: this.options.text
});
}
});
```

There is one final piece to finalizing this. The user must define a location for where their `behaviors` are stored.
A simple example of this would look like this:

```js
Marionette.Behaviors.behaviorsLookup = function() {
return window.Behaviors;
}
```

In this example you would then store your behaviors like this:

```js
window.Behaviors.ToolTip = ToolTip;
window.Behaviors.CloseWarn = CloseWarn;
```

## API

### the event proxy
Behaviors are powered by an event proxy. What this means is that any events that are triggered by the view's `triggerMethod` function are passed to each Behavior on the view as well.

As a real world example, whenever in your `view` you would have `onShow`, your behavior can also have this `onShow` method defined. The same follows for `modelEvents` and `collectionEvents`. Think of your behavior as a receiver for all of the events on your view instance.

This concept also allows for a nice decoupled method to communicate to behaviors from your view instance.
You can just call from within your view `this.triggerMethod("SomeEvent", {some: "data"})`. then your `behavior` class would look like this:

```js
Marionette.Behavior.extend({
onSomeEvent: function(data) {
console.log("wow such data", data);
}
});
```


### $
`$` is a direct proxy of the views `$` lookup method.
```js
Marionette.Behavior.extend({
onShow: function() {
this.$('.zerg')
}
});
```

### $el
`$el` is a direct proxy of the views `el` cached as a jquery selector.
```js
Marionette.Behavior.extend({
onShow: function() {
this.$el.fadeOut('slow')
}
});
```

### defaults
`defaults` can be a `hash` or `function` to define the default options for your behavior.
The default options will be overridden depending on what you set as the options per behavior (this works just like a `backbone.model`).

```js
Marionette.Behavior.extend({
defaults: function() {
return {
'deepSpace': 9
}
}
});
```

```js
Marionette.Behavior.extend({
defaults: {
'dominion': 'invasion',
'doge': 'amaze'
}
}
});
```

### view
The `view` is a reference to the view instance that the `behavior` is on.

```js
Marionette.Behavior.extend({
onShow: function() {
this.view.close();
}
});
```
72 changes: 72 additions & 0 deletions docs/marionette.behaviors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Marionette.Behaviors

'Marionette.Behaviors' is a utility class that takes care of glueing your `behavior` instances to their given `View`.
The most important part of this class is that you **MUST** override the class level `behaviorsLookup` method for things to work properly.

## Documentation Index
* [API](#api)
* [Behaviors Lookup](#behaviorsLookup)
* [getBehaviorClass](#getBehaviorClass)
* [behaviorClass](#behaviorClass)

## API

There are two class level methods that you can override on the `Behaviors` class. The rest of the class is tied to under the hood implementation details of views.

### behaviorsLookup

This method defines where your behavior classes are stored. A simple implementation might look something like this.

```js
Marionette.Behaviors.behaviorsLookup = function() {
return window.Behaviors;
}
```

By default the behaviors are looked up by their key value in a given views behavior hash.

In this sample (using the default `getBehaviorClass` implementation) your code will expect the following behaviors to be present in `window.Behaviors.CloseWarn` and `window.Behaviors.ToolTip`

```js
var MyView = Marionette.ItemView.extend({
behaviors: {
CloseWarn: {
message: "you are closing all your data is now gone!"
},
ToolTip: {
text: "what a nice mouse you have"
}
}
});
```

### getBehaviorClass

This method has a default implementation that is simple to override. It is responsible for the lookup of single behavior from within the `Behaviors.behaviorsLookup` or elsewhere.

```js
getBehaviorClass: function(options, key) {
if (options.BehaviorClass) {
return options.BehaviorClass;
}

return Behaviors.behaviorsLookup[key];
}
```

### behaviorClass

This property lets you pass a `class` in for the `behavior` to use (bypassing the normal key based lookup). This is nice to have when the behavior is a dependency of the view in [requirejs](http://requirejs.org/). Properties passed in this way will be used in `getBehaviorClass`.

```js
define(['lib/tooltip'], function(Tooltip) {
var View = Marionette.ItemView.extend({
behaviors: {
Tooltip: {
behaviorClass: Tooltip,
message: "hello world"
}
}
});
});
```
5 changes: 5 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,11 @@ examples on how to get started, please visit [the Wiki](https://github.com/mario
* [**Marionette.Layout**](docs/marionette.layout.md): A view that renders a layout and creates region managers to manage areas within it
* [**Marionette.View**](docs/marionette.view.md): The base View type that other Marionette views extend from (not intended to be used directly)

**Behaviors**

* [**Marionette.Behavior**](docs/marionette.behavior.md): an encapsulated `View` interaction layer that can be mixed into any `view`, helping to DRY up your view code.
* [**Marionette.Behaviors**](docs/marionette.behaviors.md): A helper class to glue your behaviors to your views.

**View Management**

* [**Marionette.Region**](docs/marionette.region.md): Manage visual regions of your application, including display and removal of content
Expand Down
Loading

0 comments on commit 8b09a28

Please sign in to comment.