Controllers are responsible for handling a request and producing a response. After routing has determined what controller to use, an action function will be invoked. This action function does the work of loading data and rendering views.
In Locomotive, controllers are instances of Controller
. Defining a new
controller is as simple as creating an instance and exporting it via the module.
var PhotosController = new Controller();
module.exports = PhotosController;
Functions attached to the controller are known as action functions. When Locomotive receives a request, it will create a new instance of the controller and call the appropriate action function.
For example, if a request is received for /photos/123
, Locomotive will call
the show()
action of PhotosController
.
PhotosController.show = function() {
this.render();
}
Controllers are responsible for sending a response to the request. This is
typically accomplished by render()
ing a view or issuing a redirect()
.
Requests often contain data that the controller needs to access when building a
response. The param()
function is used to get data contained in the route,
query, or body parameters.
SearchController.find = function() {
this.query = this.param('query');
// execute search query...
this.render('results');
}
The value returned by param()
will be found by checking parameters in the
following order:
Rendering a view is accomplished by calling render()
. Any instance variables
attached to the controller will be made available to the view. By convention,
variables named with a leading underscore are considered private and will not be
made available to the view.
PhotosController.show = function() {
// this._photo is "private", and not available in the view
this.title = this._photo.title;
this.description = this._photo.description;
this.render();
}
The view found in views/photos/show.html.ejs
will be rendered.
<h2><%= title %></h2>
<p><%= description %></p>
By default, the view corresponding to the current action will be rendered, in
this case show
. A different action's view can be rendered by specifying
it as an argument:
this.render('index');
If you want to render a view belonging to an action in an entirely separate controller, that can be accomplished as follows:
this.render('albums/show');
Options can be used to render a different format or use a different template engine.
this.render({ format: 'xml' });
// => renders `action.xml.ejs`
this.render({ format: 'xml', engine: 'xmlb' });
// => renders `action.xml.xmlb`
Refer to formats for settings used to associate a format with a template engines.
By default, the output of rendering is automatically sent as a response to the
request. By passing a callback as the final argument to render()
, the output
can instead be captured.
this.render('email', { name: 'Jack' }, function(err, html) {
// ...
})
This is useful in cases where you want to use a template to generate an email message, rather than a response.
Instead of rendering a view, a controller may want to redirect the request.
this.redirect('/login');
If an application needs access to the raw request and response, each is available as an instance variable within the controller.
PhotosController.show = function() {
var req = this.req; // also aliased as this.request
var res = this.res; // also aliased as this.response
this.render();
}
In Locomotive, requests are dispatched through Express and any middleware in use before arriving at the controller. As such, the entirety of the Express API and the core Node.js HTTP API apply to these objects.
In most cases, the controller provides functions to access commonly needed
properties on the request and response, such as param()
. It's recommended to
use these functions whenever possible.