The Learn page will cover the following:
Controller is an event delegation library that helps logically organize your event handlers. The event handlers defined in controllers never need to be reattached to elements in the page, even if you add new elements after the page loads. Controller helps you organize your application code by grouping event handlers assigned to similar HTML elements.
Actions are the functions in each controller that respond to events. Each action is an event handler that responds to a DOM event. They way you name an action defines what elements and event it responds to.
Params is the single parameter passed into actions. It is an instance the Params class, which gives it access to the event and target element that have called this event handler, as well as several helper methods to simplify your event handling code.
There are two separate ways to use Controller, as a standalone script or a plugin of JavaScriptMVC.
Download Controller.zip and add a script tag that loads controller.js.
<script src="controller.js" type="text/javascript"></script>
Simply include the controller plugin.
include.plugins('controller');
A controller's name links it to specific elements in the page. In the following example, the Employees Controller would match the ol element with id 'employees'.
<ol id="employees">
<li class="employee">Robert Kelly</li>
<li class="employee">Dre</li>
<li class="employee">Snoop</li>
</ol>
// event handlers for "employee" li's
Controller("employees",{
// the onclick event handler
click: function(){
alert("clicked "+params.element.value);
// --> "clicked Dre"
}
});
The containing element's id matches the name of the controller. In the Employees Controller, event handlers are assigned to elements with class 'employee'. In the example above, click is an event handler assigned to the li elements with class 'employee'.
There are two naming rules that define how controllers bind themselves to HTML elements:
The functions within each controller are (usually) event handlers. If it has an event handler name, its called an Action. Actions' names describe the elements and events they respond to.
The most common way to name your event handler is with the event's name, such as mouseover, dblclick, or keypress. These event handlers respond to the events they are named after and are assigned to elements inside the controller's top level element with a class name matching the controller's name singularized.
Controller("employees",{
click: function(){ alert("clicked employee"); }
});
The click handler in the example above is assigned to all li elements with class 'employee'.
Action naming lets you kill two birds with one stone with CSS queries. With your action name, you can assign your event handler to any elements that match a CSS query.
In the example below, there are elements we want to assign event handlers to that don't have class 'todo'. Using CSS queries in the action name, we can assign handlers to these elements.
<div id='todos'>
<a id='close'>Close</a>
<div class='todo'>Brush teeth <img class='delete' src='delete.png'></div>
<div class='todo'>Sort recycling <img class='delete' src='delete.png'></div>
<div class='todo'>Remove business socks <img class='delete' src='delete.png'></div>
</div>
Controller('todos', {
// match all img tags with class 'delete' inside of 'todo' elements
// there is an implied '.todo' in the CSS Query string
"img.delete click": function(params){
params.class_element().parentNode.removeChild(params.class_element());
},
Any img element with class 'delete' that is contained within a 'todo' element will be assigned this click handler. It is important to note that the search for elements starts at the 'todo' element by default.
Often, you'll want to match elements that are not found by the default CSS query string. For example, what if we want to match something that isn't within a 'todo' element? The next example shows how to do this.
"# #close click": function(params){
params.element.parentNode.style.display = 'none';
}
The above example adds a click handler to elements matching the CSS selector '#todos #close'. If you want to match an element that is not within the li elements with class 'todo' but is within the element with id 'todos', use a # to start the CSS query. Handler names that begin with '#' replace '#' with '#controller_name' in their CSS selector.
Controller provides a simple, clean solution to AJAX callbacks. A prototype method of Controller, continue_to, creates a continutation that calls the controller function with the passed name when execution returns.
click: function(params){
this.cupcakes = 'frosty';
new Ajax.Request('delete.php', {onComplete: this.continue_to('deleted')});
},
deleted: function(params){
alert('item deleted');
// the controller instance is preserved along with its data
alert(this.cupcakes);
// --> 'frosty'
}
In the above example, an AJAX request is made to delete.php. When a response is received from the server, the deleted function will be called. The controller instance is the same one used in the click action, so any data set in click is also available in deleted.
Event handlers defined in controllers are passed a parameter called params. This object is an instance of the Controller.Params class.
Params.element is the element that triggered this event handler.
params.element.style.backgroundColor = '';
Params.event is the event that triggered this event handler.
// call kill() to stop propagation/bubbling of the event
params.event.kill();
In submit event handlers, you call form_params for an object with the name, value pairs of the form. This is useful for client side form validation.
var data = params.form_params();
Class_element returns the top level container element.
// returns element with id "todos" for the Todos Controller
params.class_element().style.className = 'submitted';
If you want to respond to events like:
you create a special main controller by naming it 'main'. The main controller listens for events on the window, document, and body.
Controller('main',{
// the window onload event handler
load: function(){
alert('page loaded');
},
// the window resize event handler
resize: function(){
alert('page resized');
}
});
Here is how Controller works behind the scenes. Controller event handlers are attached high in the DOM. They look for events as they bubble up the DOM. When they see the event they are looking for, they check to see if their CSS selector matches the target element of the event. If so, this event handler function is called.
This technique is known as event delegation and it allows event handlers to remain permanently attached to page elements. You can create event handlers for page elements that aren't yet on the page, and at some later point when they are added, the event handlers will automatically work.
If you find yourself building HTML strings, you probably realized it gets messy. View helps clean up this type of code with client side templates. Controller has a render function to make this easy. To add this functionality, include the 'controller_view' plugin.
Controller('todos',{
click: function(){
this.title = 'Supplies';
this.supplies = ['hammer','nails','wood'];
this.render({to: 'element_id'});
}
});
<h1><%= title %></h1>
<ul>
<% for(var i=0; i<supplies.length; i++) { %>
<li>
<a href='supplies/<%= supplies[i] %>'>
<%= supplies[i] %>
</a>
</li>
<% } %>
</ul>
The above example loads a template from app/views/todos/click.ejs. It renders this template using the data present in the controller instance. It updates the element with id 'element_id' with the resulting HTML.
You can share EJS templates with other actions using the 'action' or 'partial' options for render.
Just kidding, no event handler ever needs re-attachment. Controller event handlers use event delegation. Thus the event handlers remain permanently attached to page elements. Later, when you add a page element, like this:
$('todos').innerHTML += "<li class='todo'>Laundry</li>";
// this li automatically inherits event handlers from Todos Controller
you don't need to worry about attaching event handlers to it. The event handlers in the Todos Controller are automatically assigned to the li element.
Overview
Controller's highlights.
API
Low level documentation for Controller's methods.
Demo
A full walkthrough of an interactive todo list built with Controller.