Batman.Controller
Batman.Controller is the base class from which all an application's controllers should descend from. Batman.Controllers are responsible for executing actions which fire off requests for model data, render views, or redirect to other actions in response to navigation state changes.
-
Controller Directory
Batman.Controllers are singletons which means each controller in your application is instantiated exactly once. The instance of each controller is available on the class at the@sharedControllerproperty, or within aControllerDirectoryon theApplicationclass. SeeBatman.Application.controllers. -
Actions
Each
Batman.Controllershould have a number of instance level functions which can fetch the data needed and often render a view (or views) to display that data. These functions can be declared in typical CoffeeScript fashion like so:class Alfred extends Batman.App @root "todos#index" class Alfred.TodosController extends Batman.Controller index: (params) -> show: (params) ->Each action function receives the parameters from the dispatcher which are pulled out of the navigated-to URL. This includes both named route parameters (
/:foostyle) as well as arbitrary query parameters (?foo=barstyle). -
routingKey and Minification
For functionality like contextual redirects and automatic view source and class inference, Batman needs to know the name of all your
Controllersubclasses. The usual way to do this is by usingFunction::namewhich Batman will use in development, but this is often mangled in minified production environments. For this reason, Batman will error out on boot if aControllersubclass'routingKeyis not defined on the prototype. TheroutingKeyis aStringwhich remains unmangled after minification and thus allows Batman to continue using the aforementioned features. To disable this requirement (if you know your code won't ever be minified), setBatman.config.minificationErrorsto false. -
render([options : [Object|boolean]])
renderis used to control the rendering of the currently executed action. Passingfalsewill prevent the current action from implicitly rendering the view. Passing anoptionsobject allows the defaultview,source,viewClassoryieldblock to be overridden, all of which are optional. If no parameters are passed to options, it will manually trigger the render function, rather than waiting for it to be implicitly called at the end of the action.Note: For more information on yield blocks, see Controller::.defaultRenderYield
class TestController extends Batman.Controller routingKey: 'test' nonVisibleAction: -> @render(false) customSource: -> @render(source: 'errors/404') customYield: -> @render(into: 'not-main') delayedRender: -> setTimeout (-> @render()), 1000 @render(false) -
@beforeAction([options : [string|Object],] filter : [string|Function])
@beforeActionallows controllers to declare that a function should be executed before the body of an action during action execution.@beforeActionoptionally accepts some options representing which action(s) to execute the filter before, and then a string naming a function or function proper to execute.The
optionsargument can take three forms to imply different things:undefined: implies that this filter function should be executed before all actions.- a String: implies that this filter function should be executed before the action named by the string.
- an Object: implies that this filter function should be executed before the actions named by an Array at the
onlykey in the options object, or before all actions excluding those named by an Array at theexceptkey in the options object.
test "@beforeAction allows declaration of filters to execute before an action", -> results = [] class TestController extends Batman.Controller routingKey: "test" @beforeAction only: "index", -> results.push "before!" index: -> results.push "action!" @render false controller = TestController.get("sharedController") controller.dispatch "index" equal results[0], "before!" equal results[1], "action!" test "@beforeAction allows declaration of named filters to execute before an action", -> class TodoController extends Batman.Controller routingKey: "test" @beforeAction only: "show", "fetchTodo" fetchTodo: -> @set("todo", {isDone: true}) show: -> @render false controller = TodoController.get("sharedController") controller.dispatch "show" deepEqual controller.get("todo"), {isDone: true} test "@beforeAction allows whitelisting or blacklisting filters to execute before an action", -> class TodoController extends Batman.Controller routingKey: "test" @beforeAction only: ["show", "edit"], "fetchTodo" @beforeAction except: ["index"], "prepareNewTodo" fetchTodo: -> @set "todo", {isDone: true} prepareNewTodo: -> @set "newTodo", {isDone: false} index: -> @render false show: -> @render false controller = TodoController.get("sharedController") controller.dispatch "show" deepEqual controller.get("todo"), {isDone: true} deepEqual controller.get("newTodo"), {isDone: false} test "@beforeAction allows declaration of filters to execute before all actions", -> results = [] class TestController extends Batman.Controller routingKey: "test" @beforeAction -> results.push "before!" index: -> results.push "action!" @render false controller = TestController.get("sharedController") controller.dispatch "index" equal results[0], "before!" equal results[1], "action!" -
@afterAction([options : [string|Object],] filter : [string|Function])
@afterActionallows controllers to declare that a function should be run after the action body and all operations have completed during action execution. Functions declared by@afterActionwill thus be run after the code of the action body and also after any redirects or renders have taken place and completed.@afterActionoptionally accepts some options representing which action(s) to execute the filter after, and then a string naming a function or function proper to execute. SeeBatman.Controller.beforeActionfor documentation on the structure of the options.test "@afterAction allows declaration of filters to execute after an action", -> results = [] class TestController extends Batman.Controller routingKey: "test" @afterAction only: "index", -> results.push "after!" index: -> results.push "action!" @render false controller = TestController.get("sharedController") controller.dispatch "index" equal results[0], "action!" equal results[1], "after!" test "@afterAction allows declaration of named filters to execute after an action", -> result = null class TodoController extends Batman.Controller routingKey: "test" @afterAction only: "create", "notify" notify: -> result = "Todo created successfully." create: -> @render false controller = TodoController.get("sharedController") controller.dispatch "create" equal result, "Todo created successfully." test "@afterAction allows whitelisting or blacklisting filters to execute after an action", -> result = null class TodoController extends Batman.Controller routingKey: "test" @afterAction only: ["create", "update"], "notify" index: -> @render false create: -> @render false notify: -> result = "Todo created successfully." controller = TodoController.get("sharedController") controller.dispatch "index" equal result, null controller.dispatch "create" equal result, "Todo created successfully." test "@afterAction allows declaration of filters to execute after all actions", -> results = [] class TestController extends Batman.Controller routingKey: "test" @afterAction -> results.push "after!" index: -> results.push "action!" @render false controller = TestController.get("sharedController") controller.dispatch "index" equal results[0], "action!" equal results[1], "after!" -
executeAction(action: string[, params: Object])
executeActionwill run theactionspecified, running all@beforeActionand@afterActionfilters that match that endpoint. Optionally,paramscan be passed into the new action for processing. If no params are passed, it will default to the params of the current action.test "executeAction will run the called action", -> result = null class TestController extends Batman.Controller routingKey: "test" show: (params) -> result = params.id @render false other: -> @executeAction("show", {id: 4}) controller = TestController.get("sharedController") controller.dispatch "other" equal result, 4 test "executeAction will run all @beforeAction and @afterAction filters", -> results = [] class TestController extends Batman.Controller routingKey: "test" @beforeAction only: "index", -> results.push "before!" @afterAction only: "index", -> results.push "after!" index: -> results.push "action!" @render false other: -> @executeAction("index") controller = TestController.get("sharedController") controller.dispatch "other" deepEqual results, ["before!", "action!", "after!"] -
redirect(url: string)
redirectwill forward the user to a new route defined byurlwithout hard-refreshing the page. This modifies the current page URL using pushState if supported, otherwise falling back to hash-bang fragment identifiers. The url can be obtained dynamically usingMyApp.get('routes').myResourceName().path()or by manually passing a string, such as@redirect('/test'). -
scrollToHash([hash: string])
scrollToHashemulates the native scrolling behaviour of the browser by allowing the URL to link to a specific ID on the page. The code will look for an element with a matching ID tohashif it is provided otherwise using the#parameter in the URL. If an element is found, the page will scroll to the matching element. -
autoScrollToHash[= true] : boolean
autoScrollToHashis a boolean property on the instance level of a controller describing whether or not the controller should automatically scroll the browser window to the element with ID equal to thehashparameter. This behaviour emulates the native behaviour of the same nature, but is implemented in Batman so the functionality works after each dispatch (instead of each page refresh) and when Batman is using hash bang routing.autoScrollToHashis true by default. -
defaultRenderYield[= 'main'] : string
defaultRenderYieldis astringrepresenting which yield a controller should automatically render into if no yield is mentioned explicitly.defaultRenderYieldis'main'normally, which means calls to@render()or actions which rely on the implicit rendering render their views into themainyield (and end up whereverdata-yield="main"is found in your HTML).
