Skip to content

Commit ae9708b

Browse files
committed
adding updating events to controller and better model demos
1 parent 7768632 commit ae9708b

File tree

8 files changed

+338
-162
lines changed

8 files changed

+338
-162
lines changed

controller/controller.js

Lines changed: 150 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -608,7 +608,7 @@ steal('jquery/class', 'jquery/lang/string', 'jquery/event/destroyed', function(
608608
* default it is called with the element and options passed to the controller.
609609
*/
610610
setup: function( element, options ) {
611-
var funcName, ready, cls = this.Class;
611+
var funcName, ready, cls = this[STR_CONSTRUCTOR];
612612

613613
//want the raw element here
614614
element = element.jquery ? element[0] : element;
@@ -622,8 +622,7 @@ steal('jquery/class', 'jquery/lang/string', 'jquery/event/destroyed', function(
622622
//set in data
623623
(data(element) || data(element, {}))[pluginname] = this;
624624

625-
//adds bindings
626-
this._bindings = [];
625+
627626
/**
628627
* @attribute options
629628
* Options is [jQuery.Controller.static.defaults] merged with the 2nd argument
@@ -651,15 +650,7 @@ steal('jquery/class', 'jquery/lang/string', 'jquery/event/destroyed', function(
651650
*/
652651
this.options = extend( extend(true, {}, cls.defaults), options);
653652

654-
//go through the cached list of actions and use the processor to bind
655-
for ( funcName in cls.actions ) {
656-
if ( cls.actions.hasOwnProperty(funcName) ) {
657-
ready = cls.actions[funcName] || cls._action(funcName, this.options);
658-
this._bindings.push(
659-
ready.processor(ready.delegate || element, ready.parts[2], ready.parts[1], funcName, this));
660-
}
661-
}
662-
653+
663654

664655
/**
665656
* @attribute called
@@ -669,13 +660,8 @@ steal('jquery/class', 'jquery/lang/string', 'jquery/event/destroyed', function(
669660
*/
670661
this.called = "init";
671662

672-
//setup to be destroyed ... don't bind b/c we don't want to remove it
673-
var destroyCB = shifter(this,"destroy");
674-
this.element.bind("destroyed", destroyCB);
675-
this._bindings.push(function( el ) {
676-
//destroyCB.removed = true;
677-
$(element).unbind("destroyed", destroyCB);
678-
});
663+
// bind all event handlers
664+
this.bind();
679665

680666
/**
681667
* @attribute element
@@ -725,32 +711,70 @@ steal('jquery/class', 'jquery/lang/string', 'jquery/event/destroyed', function(
725711
return this.element;
726712
},
727713
/**
728-
* Bind attaches event handlers that will be removed when the controller is removed.
729-
* This is a good way to attach to an element not in the controller's element.
730-
* <br/>
731-
* <h3>Examples:</h3>
732-
* @codestart
733-
* init: function() {
734-
* // calls somethingClicked(el,ev)
735-
* this.bind('click','somethingClicked')
736-
*
737-
* // calls function when the window is clicked
738-
* this.bind(window, 'click', function(ev){
739-
* //do something
740-
* })
741-
* },
742-
* somethingClicked: function( el, ev ) {
743-
*
744-
* }
745-
* @codeend
746-
* @param {HTMLElement|jQuery.fn} [el=this.element] The element to be bound
714+
* Bind attaches event handlers that will be
715+
* removed when the controller is removed.
716+
*
717+
* This used to be a good way to listen to events outside the controller's
718+
* [jQuery.Controller.prototype.element element]. However,
719+
* using templated event listeners is now the prefered way of doing this.
720+
*
721+
* ### Example:
722+
*
723+
* init: function() {
724+
* // calls somethingClicked(el,ev)
725+
* this.bind('click','somethingClicked')
726+
*
727+
* // calls function when the window is clicked
728+
* this.bind(window, 'click', function(ev){
729+
* //do something
730+
* })
731+
* },
732+
* somethingClicked: function( el, ev ) {
733+
*
734+
* }
735+
*
736+
* @param {HTMLElement|jQuery.fn|Object} [el=this.element]
737+
* The element to be bound. If an eventName is provided,
738+
* the controller's element is used instead.
739+
*
747740
* @param {String} eventName The event to listen for.
748741
* @param {Function|String} func A callback function or the String name of a controller function. If a controller
749742
* function name is given, the controller function is called back with the bound element and event as the first
750743
* and second parameter. Otherwise the function is called back like a normal bind.
751744
* @return {Integer} The id of the binding in this._bindings
752745
*/
753746
bind: function( el, eventName, func ) {
747+
if( el === undefined ) {
748+
//adds bindings
749+
this._bindings = [];
750+
//go through the cached list of actions and use the processor to bind
751+
752+
var cls = this[STR_CONSTRUCTOR],
753+
bindings = this._bindings,
754+
actions = cls.actions,
755+
element = this.element;
756+
757+
for ( funcName in actions ) {
758+
if ( actions.hasOwnProperty(funcName) ) {
759+
ready = actions[funcName] || cls._action(funcName, this.options);
760+
bindings.push(
761+
ready.processor(ready.delegate || element,
762+
ready.parts[2],
763+
ready.parts[1],
764+
funcName,
765+
this));
766+
}
767+
}
768+
769+
770+
//setup to be destroyed ... don't bind b/c we don't want to remove it
771+
var destroyCB = shifter(this,"destroy");
772+
element.bind("destroyed", destroyCB);
773+
bindings.push(function( el ) {
774+
$(el).unbind("destroyed", destroyCB);
775+
});
776+
return bindings.length;
777+
}
754778
if ( typeof el == 'string' ) {
755779
func = eventName;
756780
eventName = el;
@@ -765,6 +789,14 @@ steal('jquery/class', 'jquery/lang/string', 'jquery/event/destroyed', function(
765789
this._bindings.push(binder(el, eventName, func, selector));
766790
return this._bindings.length;
767791
},
792+
_unbind : function(){
793+
var el = this.element[0];
794+
each(this._bindings, function( key, value ) {
795+
value(el);
796+
});
797+
//adds bindings
798+
this._bindings = [];
799+
},
768800
/**
769801
* Delegate will delegate on an elememt and will be undelegated when the controller is removed.
770802
* This is a good way to delegate on elements not in a controller's element.<br/>
@@ -793,11 +825,81 @@ steal('jquery/class', 'jquery/lang/string', 'jquery/event/destroyed', function(
793825
return this._binder(element, eventName, func, selector);
794826
},
795827
/**
796-
* Called if an controller's [jquery.controller.plugin jQuery helper] is
828+
* Update extends [jQuery.Controller.prototype.options this.options]
829+
* with the `options` argument and rebinds all events. It basically
830+
* re-configures the controller.
831+
*
832+
* For example, the following controller wraps a recipe form. When the form
833+
* is submitted, it creates the recipe on the server. When the recipe
834+
* is `created`, it resets the form with a new instance.
835+
*
836+
* $.Controller('Creator',{
837+
* "{recipe} created" : function(){
838+
* this.update({recipe : new Recipe()});
839+
* this.element[0].reset();
840+
* this.find("[type=submit]").val("Create Recipe")
841+
* },
842+
* "submit" : function(el, ev){
843+
* ev.preventDefault();
844+
* var recipe = this.options.recipe;
845+
* recipe.attrs( this.element.formParams() );
846+
* this.find("[type=submit]").val("Saving...")
847+
* recipe.save();
848+
* }
849+
* });
850+
* $('#createRecipes').creator({recipe : new Recipe()})
851+
*
852+
*
853+
* @demo jquery/controller/demo-update.html
854+
*
855+
* Update is called if a controller's [jquery.controller.plugin jQuery helper] is
797856
* called on an element that already has a controller instance
798-
* of the same type. The base method
799-
* extends [jQuery.Controller.prototype.options this.options]
800-
* with the options passed in. If you overwrite this, you might want to call
857+
* of the same type.
858+
*
859+
* For example, a widget that listens for model updates
860+
* and updates it's html would look like.
861+
*
862+
* $.Controller('Updater',{
863+
* // when the controller is created, update the html
864+
* init : function(){
865+
* this.updateView();
866+
* },
867+
*
868+
* // update the html with a template
869+
* updateView : function(){
870+
* this.element.html( "content.ejs",
871+
* this.options.model );
872+
* },
873+
*
874+
* // if the model is updated
875+
* "{model} updated" : function(){
876+
* this.updateView();
877+
* },
878+
* update : function(options){
879+
* // make sure you call super
880+
* this._super(options);
881+
*
882+
* this.updateView();
883+
* }
884+
* })
885+
*
886+
* // create the controller
887+
* // this calls init
888+
* $('#item').updater({model: recipe1});
889+
*
890+
* // later, update that model
891+
* // this calls "{model} updated"
892+
* recipe1.update({name: "something new"});
893+
*
894+
* // later, update the controller with a new recipe
895+
* // this calls update
896+
* $('#item').updater({model: recipe2});
897+
*
898+
* // later, update the new model
899+
* // this calls "{model} updated"
900+
* recipe2.update({name: "something newer"});
901+
*
902+
* _NOTE:_ If you overwrite `update`, you probably need to call
801903
* this._super.
802904
*
803905
* ### Example
@@ -814,10 +916,14 @@ steal('jquery/class', 'jquery/lang/string', 'jquery/event/destroyed', function(
814916
* $('#myel').thing({prop : 'val1'}); // alerts init:val1
815917
* $('#myel').thing({prop : 'val2'}); // alerts update:val2
816918
*
817-
* @param {Object} options the object passed to the [jquery.controller.plugin jQuery helper function]
919+
* @param {Object} options A list of options to merge with
920+
* [jQuery.Controller.prototype.options this.options]. Often, this method
921+
* is called by the [jquery.controller.plugin jQuery helper function].
818922
*/
819923
update: function( options ) {
820924
extend(this.options, options);
925+
this._unbind();
926+
this.bind();
821927
},
822928
/**
823929
* Destroy unbinds and undelegates all event handlers on this controller,
@@ -860,9 +966,7 @@ steal('jquery/class', 'jquery/lang/string', 'jquery/event/destroyed', function(
860966
this.element.removeClass(fname);
861967

862968
// unbind bindings
863-
each(this._bindings, function( key, value ) {
864-
value(self.element[0]);
865-
});
969+
this._unbind();
866970
// clean up
867971
delete this._actions;
868972

controller/controller_test.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,4 +241,29 @@ test("inherit defaults", function() {
241241
ok(inst.options.newProp == 'newVal', 'Instance must have defaults of it`s class');
242242
});
243243

244+
test("update rebinding", 2, function(){
245+
var first = true;
246+
$.Controller("Rebinder", {
247+
"{item} foo" : function(item, ev){
248+
if(first){
249+
equals(item.id, 1, "first item");
250+
first = false;
251+
} else {
252+
equals(item.id, 2, "first item");
253+
}
254+
}
255+
});
256+
257+
var item1 = {id: 1},
258+
item2 = {id: 2},
259+
el = $('<div>').rebinder({item: item1})
260+
261+
$(item1).trigger("foo")
262+
263+
el.rebinder({item: item2});
264+
265+
$(item2).trigger("foo")
266+
})
267+
268+
244269
});

controller/demo-update.html

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2+
"http://www.w3.org/TR/html4/strict.dtd">
3+
<html lang="en">
4+
<head>
5+
<title>Controller Example</title>
6+
<style type='text/css'>
7+
8+
</style>
9+
</head>
10+
<body>
11+
<div id="demo-html">
12+
<form action='' id='createRecipes'>
13+
<input type='text' name='name'/>
14+
<input type='submit' value='Create Recipe'/>
15+
</form>
16+
</div>
17+
<script type='text/javascript' src='../../steal/steal.js'></script>
18+
<script type='text/javascript'>
19+
steal("jquery/controller",
20+
"jquery/model",
21+
"jquery/dom/form_params",
22+
"jquery/dom/fixture",
23+
function(){
24+
25+
$.fixture.delay = 2000;
26+
$.fixture("POST /recipes",function(){
27+
return {};
28+
})
29+
30+
$.Model('Recipe',{
31+
create : "/recipes"
32+
},{});
33+
34+
$.Controller('Creator',{
35+
"{recipe} created" : function(){
36+
this.update({recipe : new Recipe()});
37+
this.element[0].reset();
38+
this.find("[type=submit]").val("Create Recipe")
39+
},
40+
"submit" : function(el, ev){
41+
ev.preventDefault();
42+
var recipe = this.options.recipe;
43+
recipe.attrs( this.element.formParams() );
44+
this.find("[type=submit]").val("Saving...")
45+
recipe.save();
46+
}
47+
});
48+
49+
$('#createRecipes').creator({recipe : new Recipe()})
50+
51+
})
52+
</script>
53+
</body>
54+
</html>

controller/route/route.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
steal('jquery/dom/route','jquery/controller', function(){
2+
/**
3+
*
4+
* ":type route" //
5+
*
6+
* @param {Object} el
7+
* @param {Object} event
8+
* @param {Object} selector
9+
* @param {Object} cb
10+
*/
11+
jQuery.Controller.processors.route = function(el, event, selector, cb){
12+
13+
}
14+
})

0 commit comments

Comments
 (0)