Skip to content

Commit

Permalink
Merge branch 'sjs/ui-events-and-triggers-coverage'
Browse files Browse the repository at this point in the history
  • Loading branch information
samccone committed Dec 4, 2013
2 parents bca87aa + 426209f commit 27dbd8d
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 14 deletions.
2 changes: 2 additions & 0 deletions docs/marionette.itemview.md
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,8 @@ same UI element more than once in your view's code. Instead of
duplicating the selector, you can simply reference it by
`this.ui.elementName`:

You can also use the ui hash values from within events and trigger keys using the ```"@ui.elementName"```: syntax

```js
Backbone.Marionette.ItemView.extend({
tagName: "tr",
Expand Down
59 changes: 46 additions & 13 deletions docs/marionette.view.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Marionette has a base `Marionette.View` type that other views extend from.
This base view provides some common and core functionality for
other views to take advantage of.

**Note:** The `Marionette.View` type is not intended to be
**Note:** The `Marionette.View` type is not intended to be
used directly. It exists as a base view for other view types
to be extended from, and to provide a common location for
behaviors that are shared across all views.
Expand All @@ -16,6 +16,7 @@ behaviors that are shared across all views.
* [View onBeforeClose](#view-onbeforeclose)
* [View "dom:refresh" / onDomRefresh event](#view-domrefresh--ondomrefresh-event)
* [View.triggers](#viewtriggers)
* [View.events](#viewevents)
* [View.modelEvents and View.collectionEvents](#viewmodelevents-and-viewcollectionevents)
* [View.serializeData](#viewserializedata)
* [View.bindUIElements](#viewbinduielements)
Expand Down Expand Up @@ -109,8 +110,8 @@ v.close(); // view will remain open
Triggered after the view has been rendered, has been shown in the DOM via a Marionette.Region, and has been
re-rendered.

This event / callback is useful for
[DOM-dependent UI plugins](http://lostechies.com/derickbailey/2012/02/20/using-jquery-plugins-and-ui-controls-with-backbone/) such as
This event / callback is useful for
[DOM-dependent UI plugins](http://lostechies.com/derickbailey/2012/02/20/using-jquery-plugins-and-ui-controls-with-backbone/) such as
[jQueryUI](http://jqueryui.com/) or [KendoUI](http://kendoui.com).

```js
Expand All @@ -126,9 +127,28 @@ Backbone.Marionette.ItemView.extend({
For more information about integration Marionette w/ KendoUI (also applicable to jQueryUI and other UI
widget suites), see [this blog post on KendoUI + Backbone](http://www.kendoui.com/blogs/teamblog/posts/12-11-26/backbone_and_kendo_ui_a_beautiful_combination.aspx).

## View.events
Since Views extend from backbone`s view class, you gain the benifit of the [events hash](http://backbonejs.org/#View-delegateEvents).

Some preprocessing sugar is added on top to add the ability to cross utilize the ```ui``` hash.

```js
MyView = Backbone.Marionette.ItemView.extend({
// ...

ui: {
"cat": ".dog"
},

events: {
"click @ui.cat": "bark" //is the same as "click .dog":
}
});
```

## View.triggers

Views can define a set of `triggers` as a hash, which will
Views can define a set of `triggers` as a hash, which will
convert a DOM event into a `view.triggerMethod` call.

The left side of the hash is a standard Backbone.View DOM
Expand All @@ -151,7 +171,7 @@ view.on("something:do:it", function(args){
alert("I DID IT!");
});

// "click" the 'do-something' DOM element to
// "click" the 'do-something' DOM element to
// demonstrate the DOM event conversion
view.$(".do-something").trigger("click");
```
Expand All @@ -171,7 +191,7 @@ Backbone.Marionette.CompositeView.extend({
});
```

You can also specify the `triggers` as a function that
You can also specify the `triggers` as a function that
returns a hash of trigger configurations

```js
Expand All @@ -184,6 +204,19 @@ Backbone.Marionette.CompositeView.extend({
});
```

Trigger keys can be configured to cross utilize the ```ui``` hash.

```js
Backbone.Marionette.ItemView.extend({
ui: {
'monkey': '.guybrush'
},
triggers: {
'click @ui.monkey': 'see:LeChuck' // equivalent of "click .guybrush"
}
});
```

Triggers work with all View types that extend from the base
Marionette.View.

Expand Down Expand Up @@ -259,7 +292,7 @@ the model and collection events re-bound.
### Multiple Callbacks

Multiple callback functions can be specified by separating them with a
space.
space.

```js
Backbone.Marionette.CompositeView.extend({
Expand Down Expand Up @@ -346,9 +379,9 @@ do not provide a helper method mechanism while Handlebars
templates do.

A `templateHelpers` attribute can be applied to any View object that
renders a template. When this attribute is present its contents
will be mixed in to the data object that comes back from the
`serializeData` method. This will allow you to create helper methods
renders a template. When this attribute is present its contents
will be mixed in to the data object that comes back from the
`serializeData` method. This will allow you to create helper methods
that can be called from within your templates.

### Basic Example
Expand Down Expand Up @@ -412,10 +445,10 @@ templateHelpers: {
### Object Or Function As `templateHelpers`

You can specify an object literal (as shown above), a reference
to an object literal, or a function as the `templateHelpers`.
to an object literal, or a function as the `templateHelpers`.

If you specify a function, the function will be invoked
with the current view instance as the context of the
If you specify a function, the function will be invoked
with the current view instance as the context of the
function. The function must return an object that can be
mixed in to the data for the view.

Expand Down
90 changes: 90 additions & 0 deletions spec/javascripts/view.uiEventAndTriggers.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
describe("view ui event trigger configuration", function(){
"use strict";

describe("@ui syntax within events and triggers", function() {
var view, view2, fooHandler, attackHandler;

var View = Backbone.Marionette.ItemView.extend({
ui: {
foo: '.foo',
bar: '#tap'
},

triggers: {
"click @ui.foo": "do:foo"
},

events: {
"click @ui.bar": "attack"
},

attack: function() {
attackHandler();
},

render: function(){
this.$el.html("<button class='foo'></button><div id='tap'></div>");
}
});

var View2 = View.extend({
triggers: function() {
return {
"click @ui.foo": {
event: "do:foo",
preventDefault: true,
stopPropagation: false
}
}
},

events: function() {
return {
"click @ui.bar": function() {
return "attack"
}
}
}
});

beforeEach(function(){
view = new View({
model: new Backbone.Model()
});

view2 = new View({
model: new Backbone.Model()
});

view.render();
view2.render();

fooHandler = jasmine.createSpy("do:foo event handler");
attackHandler = jasmine.createSpy("attack handler");

spyOn(view, "attack").andCallThrough();
view.on("do:foo", fooHandler);
view2.on("do:foo", fooHandler);
});

it("should correctly trigger an event", function(){
view.$(".foo").trigger("click");
expect(fooHandler).toHaveBeenCalled();
});

it("should correctly call an event", function(){
view.$("#tap").trigger('click');
expect(attackHandler).toHaveBeenCalled();
});

it("should correctly call an event with a functional events hash", function(){
view2.$("#tap").trigger('click');
expect(attackHandler).toHaveBeenCalled();
});

it("should correctly call an event with a functional triggers hash", function(){
view2.$(".foo").trigger("click");
expect(fooHandler).toHaveBeenCalled();
});
});
});
24 changes: 23 additions & 1 deletion src/marionette.view.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ Marionette.View = Backbone.View.extend({
// of this.options
// at some point however this may be removed
this.options = options || {};

// parses out the @ui DSL for events
this.events = this.normalizeUIKeys(_.result(this, 'events'));
Backbone.View.prototype.constructor.apply(this, args);

Marionette.MonitorDOMRefresh(this);
Expand Down Expand Up @@ -46,6 +49,25 @@ Marionette.View = Backbone.View.extend({
return _.extend(target, templateHelpers);
},

// allows for the use of the @ui. syntax within
// a given key for triggers and events
// swaps the @ui with the associated selector
normalizeUIKeys: function(hash) {
if (typeof(hash) === "undefined") {
return;
}

_.each(_.keys(hash), function(v) {
var split = v.split("@ui.");
if (split.length === 2) {
hash[split[0]+this.ui[split[1]]] = hash[v];
delete hash[v];
}
}, this);

return hash;
},

// Configure `triggers` to forward DOM events to view
// events. `triggers: {"click .foo": "do:foo"}`
configureTriggers: function(){
Expand All @@ -54,7 +76,7 @@ Marionette.View = Backbone.View.extend({
var triggerEvents = {};

// Allow `triggers` to be configured as a function
var triggers = _.result(this, "triggers");
var triggers = this.normalizeUIKeys(_.result(this, "triggers"));

// Configure the triggers, prevent default
// action and stop propagation of DOM events
Expand Down

0 comments on commit 27dbd8d

Please sign in to comment.