This lesson covers:
Think of controllers as the glue between your models and your views. For those who have done some project management, or worked with a team of developers, the flow of controllers should ready like business rules for the application.
“When this button is clicked, add that product to that cart” “When this button is clicked, open this page with that products details” “When the page is loaded, open this page and show all the products”This same text can be used to write your tests later, and and should correspond to what you want your app to do. There is no such thing as a great app that does nothing.
Straight from Backbone.
var Events = Backbone.Events = {
// Bind one or more space separated events, or an events map,
// to a `callback` function. Passing `"all"` will bind the callback to
// all events fired.
on: function(name, callback, context) {
if (!(eventsApi(this, 'on', name, [callback, context]) && callback)) return this;
this._events || (this._events = {});
var list = this._events[name] || (this._events[name] = []);
list.push({callback: callback, context: context, ctx: context || this});
return this;
},
// Bind events to only be triggered a single time. After the first time
// the callback is invoked, it will be removed.
once: function(name, callback, context) {
if (!(eventsApi(this, 'once', name, [callback, context]) && callback)) return this;
var self = this;
var once = _.once(function() {
self.off(name, once);
callback.apply(this, arguments);
});
once._callback = callback;
this.on(name, once, context);
return this;
},
// Remove one or many callbacks. If `context` is null, removes all
// callbacks with that function. If `callback` is null, removes all
// callbacks for the event. If `events` is null, removes all bound
// callbacks for all events.
off: function(name, callback, context) {
var list, ev, events, names, i, l, j, k;
if (!this._events || !eventsApi(this, 'off', name, [callback, context])) return this;
if (!name && !callback && !context) {
this._events = {};
return this;
}
names = name ? [name] : _.keys(this._events);
for (i = 0, l = names.length; i < l; i++) {
name = names[i];
if (list = this._events[name]) {
events = [];
if (callback || context) {
for (j = 0, k = list.length; j < k; j++) {
ev = list[j];
if ((callback && callback !== (ev.callback._callback || ev.callback)) ||
(context && context !== ev.context)) {
events.push(ev);
}
}
}
this._events[name] = events;
}
}
return this;
},
// Trigger one or many events, firing all bound callbacks. Callbacks are
// passed the same arguments as `trigger` is, apart from the event name
// (unless you're listening on `"all"`, which will cause your callback to
// receive the true name of the event as the first argument).
trigger: function(name) {
if (!this._events) return this;
var args = slice.call(arguments, 1);
if (!eventsApi(this, 'trigger', name, args)) return this;
var events = this._events[name];
var allEvents = this._events.all;
if (events) triggerEvents(this, events, args);
if (allEvents) triggerEvents(this, allEvents, arguments);
return this;
},
// An inversion-of-control version of `on`. Tell *this* object to listen to
// an event in another object ... keeping track of what it's listening to.
listenTo: function(object, events, callback) {
var listeners = this._listeners || (this._listeners = {});
var id = object._listenerId || (object._listenerId = _.uniqueId('l'));
listeners[id] = object;
object.on(events, callback || this, this);
return this;
},
// Tell this object to stop listening to either specific events ... or
// to every object it's currently listening to.
stopListening: function(object, events, callback) {
var listeners = this._listeners;
if (!listeners) return;
if (object) {
object.off(events, callback, this);
if (!events && !callback) delete listeners[object._listenerId];
} else {
for (var id in listeners) {
listeners[id].off(null, null, this);
}
this._listeners = {};
}
return this;
}
};
This example is from the old jquery plugin guide, but it is still relavent. Here is jQuery’s new guide for plugins.
TODO: update this section
(function( $ ){
var methods = {
init : function( options ) {
return this.each(function(){
var $this = $(this),
data = $this.data('tooltip'),
tooltip = $('<div />', {
text : $this.attr('title')
});
// If the plugin hasn't been initialized yet
if ( ! data ) {
/*
Do more setup stuff here
*/
$(this).data('tooltip', {
target : $this,
tooltip : tooltip
});
}
});
},
destroy : function( ) {
return this.each(function(){
var $this = $(this),
data = $this.data('tooltip');
// Namespacing FTW
$(window).unbind('.tooltip');
data.tooltip.remove();
$this.removeData('tooltip');
})
},
reposition : function( ) { // ... },
show : function( ) { // ... },
hide : function( ) { // ... },
update : function( content ) { // ...}
};
$.fn.tooltip = function( method ) {
if ( methods[method] ) {
return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
} else if ( typeof method === 'object' || ! method ) {
return methods.init.apply( this, arguments );
} else {
$.error( 'Method ' + method + ' does not exist on jQuery.tooltip' );
}
};
})( jQuery );
I don’t know why but when people describe the prototype object they always make it complicated. Granted, it is there to add some layer of object orientation to Javascript, but it is not like Java’s classes. It is simpiler.
function Cart(options) {
// add any data to the cart here
this.name = options.name;
this.products = [];
}
Cart.prototype.addToCart = function(product) {
this.products.push(product);
};
Cart.prototype.getTotal = function() {
var i,t = this.products.length, products = this.products, total = 0;
for(i=0; i>t; i++) {
total = total + products[i].price;
}
return total;
};
Cart.prototype.checkout = function(creditCard) {
creditCard.charge( this.getTotal() );
};
var shoppingCart = new Cart({ name: 'shopping cart' });
shoppingCart.addToCart(new Product({ title: "King Wizard", artist: "Kid Cudi", price : 15 }));
As you can see, we are simply adding onto the prototype object attached to the function Cart. Those additions are passed onto any new Cart as methods. That’s it.
Conceptually, its almost the same as your C++ or Python classes. There is a little magic that is needed to get inheritance working the way a server side developer would expect, but it is there.
Honestly, if you are not writing a web application with a lot of moving parts, you should avoid using the prototype object. If you are only creating one instance of the cart, in the example above, and one or two products, you will not see the performance increase. In fact, I have seen some libraries that a little like prototypes, there is a function and an object of functions attached to it, but they just don’t use the ‘new’ keyword.
Further Reading:
Javascript Patterns is where I based these examples and is a good read.