Controller

In FlightPath, controllers (the C in MVC) are represented by the Controller class.  See Overview

Summary
ControllerIn FlightPath, controllers (the C in MVC) are represented by the Controller class.
OverviewIn FlightPath, controllers (the C in MVC) are represented by the Controller class.
Conventions
Defining ControllersController definitions can be stored in app/controllers/<controller_name>_controller.sjs or in a module:app/modules/module_name/controllers/<controller_name>_controller.sjs.
Properties
$page$page metadata object
datalocal request-specific data to be passed to views
nameThe name of this controller
Functions
addLayoutadds a layout layer to this this request
setLayoutSets a single layout layer to this this request, clearing any existing layouts
addFilterAdds an action filter
applyBehaviorloads a behavior and applies it to this object
getFiltersreturns the filter array for the requested event and action.
addActionAdd an action function to this controller
getActionsreturns an array of objects for each action that contain the action name and action function.
callActionCall an action as an inline function.
includeCall an action and includes its output in the current request
initInitializes a controller instance
renderrenders a view
renderContentDirectly render content, bypassing default render
renderFileDirectly render a file to the browser, bypassing default render
getElementrenders an HTML template returns the content as a string
setSets a property on data

Overview

In FlightPath, controllers (the C in MVC) are represented by the Controller class.

Controllers are responsible for connecting requests to Models and Views The consist of one or more actions: functions that take parameters from the route, perform any necessary business logic with Models and render a response, typically with Views

How to load a controller

Controllers are normally loaded via a route (See: Routes), but they can also be accessed via $FP.getController.

See

Conventions

  • Controller Names are singular, ProperCase, ex: EmployeeAction
  • Controller File names are in file_case, ending with “_controller”, ex: app/controllers/employee_action_controller.sjs
  • Controller definitions are discovered in this order: framework/controllers/<controller_name>_controller.sjs, app/controllers/<controller_name>.sjs, app/modules/<module_name>/controllers/<controller_name>_controller.sjs
  • The ini() and functions in app/controllers/global.sjs are applied before any controller definitions discovered
  • Actions are functions defined in the Controller definition file.
  • init() and any functions starting with “_” are not considered Actions, and cannot be called from Routes
  • Action names are camelCase, ex: changeStartDate()
  • Actions and are typically verbs that might be applied to the controller, e.g.EmployeeAction.edit() rather than EmployeeAction.editAction()
  • in URLs, controller and action names are in url-case, e.g. employee_action/edit/15 or employee-action/edit/15
  • The default route for FlightPath will connect a URL of employee_action/edit/15 to EmployeeAction.edit({id:15})
  • Controllers will look for a Model with the same name as the controller, and store a reference to it in this.model and this.<ModelName>
  • After executing an Action, Controllers will automatically load the View with the same name as the action, ex: EmployeeAction.edit() will render app/views/employee_action/edit.ejs
  • Calling render or renderContent will cancel default view rendering
  • Any data inteded to be rendered should be passed to the via via data or set

Defining Controllers

Controller definitions can be stored in app/controllers/<controller_name>_controller.sjs or in a module:app/modules/module_name/controllers/<controller_name>_controller.sjs.  The primary source of configuration is the init function.  This is executed after the init() function in app/controllers/global.sjs.  Any functions or properties defined in this file will be added to final Controller instance.

Functions called from init

Examples

// app/controllers/employee_action.sjs

function init(){
// See: Behavior: MynaAuth in the docs
// If this was in app/controlelr.global.sjs, it would aplpy to all
// controllers, not just EmployeeActino
this.applyBehavior("MynaAuth",{
whitelist:[],
providers:Myna.Permissions.getAuthTypes(),
redirectParams:{}
})
}

function edit(params){
// Generate ID for new record if none-supplied. That way ID will be in
// the URL and refreshing the page won't create a new record
if (!params.id){
$FP.redirectTo({
id:this.model.genKey()
})
}

// only get here if not re-directed
this.set("bean",this.model.get(params);
// this controller will automatically render
// app/views/employee_action/edit.ejs
}

See

Properties

$page

$page metadata object

This can contain arbitrary metadata about the current page, but the following properties are used by the default layout file in app/views/layouts/default.ejs

Default Properties

cssString Array. each entry should be either a complete <link> tag or a URL to a css file, e.g $FP.url +”static/css/global.css”.  If a URL does NOT start with “/” or “http” it is assumed to be relative to app/static/
iconString. favicon url.  Defaults to $server.rootUrl +”favicon.ico”
scriptsString/Object Array.  String entries should be URLs to JavaScript source files.  Object entries should be in the form of {type:”mime/type”,src:”url”}.  If a URL does NOT start with “/” or “http” it is assumed to be relative to app/static/
titleString.  Page Title.  Defaults to $controller.name + “.”+$params.action
tagsObject Array.  Meta tags to add to header.  Entries in the form of {name:”meta tag name”,content:”tag content”}
keywordsString Array.  “keywords” meta tag.  Each entry will be joined into a comma-separated list
descriptionString.  Description meta tag
contentSet by render when including layout files.  This represents the current content that needs to be included in the layout

Example

//in test_controller.sjs
function init(){
This.$page.title ="Test"
}

function index(params){
this.$page.css.push("css/default.css")
this.$page.scripts.push("/app/admin.js")
}

//loads js and CSS to support a ExtJs application with Ext.Direct support
function extIndex(params){
// using concat instead of directly assigning values preserves any
// existing values set in init()

this.$page.css =this.$page.css.concat([
"extjs/resources/css/ext-all.css",
"css/default.css"
])
this.$page.scripts =this.$page.scripts.concat([
"extjs/ext-all-debug.js",
// this is a JSONP callback to load the API from Controller: Direct
$FP.helpers.Html.url({
controller:"Direct",
action:"api",
params:{
callback:"Ext.Direct.addProvider",
namespace:"$FP"
}
})
])
}

data

local request-specific data to be passed to views

See

name

The name of this controller

Functions

addLayout

Controller.prototype.addLayout = function addLayout(layout)

adds a layout layer to this this request

Parameters

layoutString.  Layout name, path relative to app/layouts, or Myna.File pointing to a specific layout file

This layout will in included after the global default layout, the controller default layout, and any previously defined layouts for this controller

See

setLayout

Controller.prototype.setLayout = function setLayout(layout)

Sets a single layout layer to this this request, clearing any existing layouts

Parameters

layoutString,Myna.File,False.  Layout name, path relative to app/layouts, Myna.File pointing to a specific layout file, or false to disable all layouts

Detail

disables global and controller default layouts.  If layout is a layout name or path, then that will be the only layout applied

See

addFilter

Controller.prototype.addFilter = function addFilter(filter,
options)

Adds an action filter

Parameters

filterFunction or Array.  Filter function to execute, or Array of filter functions to execute.  See Filter Function Parameters below.  Note that the “this” scope when executed will be the controller instance being filtered
optionsOptional, default see below JS struct, see Options below

Options

whenOptional, default “beforeAction” When the filter should be run. see Events below
onlyOptional, default null Array of String/RegExp.  If defined, limit this filter to these action names.  Mutually exclusive with except
exceptOptional, default null Array String/RegExp.  If defined, exclude this filter from these action names.  Mutually exclusive with only

Filter Function Parameters

actionString name of action requested
paramsthe “params” argument passed to the action
responseThe return from the action.  This is always null for “beforeAction” filters

Events

beforeActionfired before executing an action function.  Return false to cancel execution, return a JS object to supply new “params” to the action.  Normally used to alter params or cancel routing, such as for authentication
afterActionFired after the action is complete but before default rendering.  If the action contains an explicit render or renderContent call then that will be executed first, without triggering “beforeRender” or “afterRender”.  Return false or make a call to render or renderContent to cancel the default render.  Normally used for altering/adding properties to controller.data prior to rendering.  Might also be used for custom rendering, but “beforeRender” is generally a better choice for that.
beforeRenderFired before rendering the default view.  Return false or make a call to render or renderContent to cancel the default render.  Normally used for custom renders that replace the default, such as rendering as RSS, or returning JSON
afterRenderFired after the default render.  Any changes to output should be made through $res.clear, $res.getContent, $res.print, etc.  Normally used to convert generated output such as converting to PDF or XHTML

Detail

Filters are functions run before or after actions and/or before and after rendering the view for an action.  Filters, by default, are run before every action, but can be applied to only specific actions.  Great uses of filters are authentication and logging.  Filters are only executed in standard request handling, callAction, and include.  Direct execution of an action will not trigger filters.

Examples

function init(){
// exec an auth function before every action except
// "home" and "status"
this.addFilter(
this._auth,
{
except:["home","status"]
}
)

//log only actions whose name starts with "save"
this.addFilter(
this._audit,
{
only:[/^save/i]
}
)

this.addFilter(
this._pdfView,
{
when:"afterRender"
}
)

}


// Functions that begin with "_" are not considered actions and
// are excluded from browser requests
function _auth(controller,action,params){
if (!$cookie.getAuthUser()){
$FP.redirectTo({
controller:"Auth",
action:"login"
})
//unnecessary, becasue redirecTo halts processing, but good practice
return false; //cancels action
}
}

function _audit(controller,action,params){
Myna.log(
"audit",
controller.name + "." + action,
Myna.dump({
params:params,
user:$cookie.getAuthUser()
})
);
}

function _pdfView(controller, action, params){
if (params.format =="pdf"){
// calling render or renderContent prevents default view from rendering
this.renderContent(
Myna.xmlToPdf(
Myna.htmlToXhtml(
$res.clear()
)
),
"application/pdf",
params.controller + "." + params.action + ".pdf"
);
}
}

applyBehavior

Controller.prototype.applyBehavior = function applyBehavior(name,
options)

loads a behavior and applies it to this object

Parameters

nameThe ProperCased name of the behavior to load, or an array of these names,
optionsOptional, defaul null JS Object containing options to pass to the behavior’s init function

Detail

Loads a behavior by name from app/behaviors/controllers/ or framework/behaviors/controllers/, whichever is found first.  Behaviors are functions that are applied to the current object.  applyBehavior should be called from init

Example

//in some_controller.sjs
function init(){
// loads the built-in PDF andJSON filters
// Each of these has an "init" function that adds the appropriate
// before and after filters
this.applyBehavior([
"FormatPdf",
"FormatJson"
]);

//loads a single behavior with options
this.applyBehavior("MynaAuth",{
whitelist:[
"Main.index",
"Main.logout"
],
redirectParams:{
message: "Enter your AD domain credentials",
providers:["ldap_ad"]
}
});
}

See

getFilters

Controller.prototype.getFilters = function addFilter(event,
action)

returns the filter array for the requested event and action.

Parameters

eventevent name
actionaction name

See

addAction

Controller.prototype.addAction = function (name,
func,
overwrite)

Add an action function to this controller

Parameters

nameString Name of action to add
funcFunction action function to add
overwriteOptional, default false Boolean If true, and an action with this name already exists, it will be overwritten by this action

getActions

Controller.prototype.getActions = function getActions()

returns an array of objects for each action that contain the action name and action function.

Object Properties

actionString action name
handlerFunction reference to action function

callAction

Controller.prototype.callAction = function callAction(action,
params)

Call an action as an inline function.

Parameters

actionaction name to execute
paramsOptional, default {} params to pass to the action

Detail

Calling this function on a controller will execute the specified action “inline”, meaning without generating output, and will return whatever the action returns.  Before and after action filters are also triggered.  This can be useful for marshaling several actions in one request, or for providing alternate ways of calling actions (see <framework.controllers.DirectController>)

Example

//triggers filters, so will fail if cur user is not authorized
var empList = $FP.getController("employee").callAction("list",params);

Note

You can also call actions directly from a controller, but filters won’t be triggered.

// does not run filters, but might render output if render() or
// renderContent() is called inside this action
var empList = $FP.getController("employee").list(params);

include

Controller.prototype.include = function include(action,
params)

Call an action and includes its output in the current request

Parameters

actionaction name to execute
paramsOptional, default {} params to pass to the action

Detail

Calling this function on a controller will execute the specified action, appending any output generated to the current request.  This will also return any response from the action.  Before and after action filters are also triggered.  This can be useful for including several actions in one request.

Example

// triggers filters, so will fail if cur user is not authorized
// includes employee listing table in the current view
$FP.getController("employee").callAction("list",params);

init

Controller.prototype.init = function init(controllerName)

Initializes a controller instance

Whenever a controller is instantiated, init() is run to configure the instance.  First the init() in the controller base class is run, then the init() in app/controllers/global.sjs, and finally the init() in app/controllers/<name>_controller.sjs

This is a good place to call this.applyBehavior, this.addFilter and this.add/setLayout

See Also

render

Controller.prototype.render = function render(options)

renders a view

Parameters

optionsOptional String or Object or null.  If a string, the filename of the view in app/views to load, minus the extension.  If this is null, then rendering is canceled.  If this is an object or undefined, see Options below:

Options

controllerOptional, default current controller ProperCased controller folder within app/views to reference.  This will be converted to file format automatically
actionOptional, default current action camelCased action name.  This will be converted to file format automatically

Detail

This function is called automatically with default options whenever an action function returns.  This function can be called explicitly from inside an action function to override this default behavior

When rendering a view, all previously generated content is cleared, and the view is included.  Once the view is rendered, it is wrapped in any defined layouts (see <addLayout>) The following variables are available in view scope, and can be directly referenced:

View Scope properties

$controllerA reference to the controller that called this view
$modelA reference to this controllers default model, may be null
$paramsA reference to the params of the most recent action
$pagea reference to this controller’s $page property
getElementa reference to getElement
[data properties]all of this controller’s data properties are available as global variables
[$FP.helpers classes]”Html” refers to $FP.helpers.Html, etc

Examples

//in app/controllers/person_controller.sjs
function doStuff(params){
//bulk sets/replaces this.data
this.set({
firstName:"Bob",
lastName:"Dobb"
})

//sets an individual property
this.set("bean",this.Person.get({id:params.id}))

// implied render, same as
// this.render({controller:this.name,action:"doStuff"})
}

//in app/views/person/person_do_stuff.ejs
<%=getElement("form_wrap",{title:"Edit Employee"})%>
<form action="<%=Html.url({action:"save",id:bean.id})%>" method="post">
<table width="100%" height="1" cellpadding="0" cellspacing="0" border="0" >
<tr>
<th class="">ID:</th><td><%=bean.id%><td>
</tr>

<tr>
<th><%=bean.getLabel("first_name")%>:</th>
<td><input name="first_name" value="<%=String(bean.data.first_name||"").escapeHtml()%>"><td>
</tr>
<tr>
<th><%=bean.getLabel("last_name")%>:</th>
<td><input name="last_name" value="<%=String(bean.data.last_name||"").escapeHtml()%>"><td>
</tr>

<tr>
<td colspan="2" align="right">
<button type="submit">Save</button>
</td>
</tr>
</table>
</form>
<%=getElement("form_wrap",{type:"end"})%>


//in app/elements/form_wrap.ejs
<@if type == "end">
</div>
<@else>
<div class="form_wrap">
<div class="caption"><%=title||""%></div>
</@if>

//in app/elements/form_footer.ejs
</div>

See

renderContent

Controller.prototype.renderContent = function renderBinary(data,
contentType,
filename)

Directly render content, bypassing default render

Parameters

dataString content, or binary data from Myna.File.readBinary or from binary database query (byte [])
contentTypeOptional, default text/html for strings, application/octet-stream for binary MIME type of data.  If “” or null, the default will be used
filenameOptional, default null if defined, a “Content-disposition” response header is set to present the standard “Save or Open?” dialog to the client.  Use this if offering a file for download, but not if you expect the content to be rendered inline

See

$res.printBinary

renderFile

Controller.prototype.renderFile = function renderBinary(file)

Directly render a file to the browser, bypassing default render

Parameters

fileMynaPath or Myna.File file to render

This performs a direct render of the file.  Mime types are auto calculated and the download is optimized for large files.

See

$res.serveFile

getElement

Controller.prototype.getElement = function renderElement(element,
options)

renders an HTML template returns the content as a string

Parameters

elementname of an element template, should map to app/views/elements/element (can be a path like “common/header.ejs”) app/views/elements/<current controller name>/element.ejs, or app/views/elements/element.ejs,
optionsany extra global properties to pass to the template

Element Scope properties

$controllerA reference to the controller that called this element
$modelA reference to this controllers default model, may be null
$paramsA reference to the params of the most recent action
$pagea reference to this controller’s $page property
[data properties]all of this controller’s data properties are available as global variables
[options properties]all options properties are available as global variables
[$FP.helpers classes]”Html” refers to $FP.helpers.Html, etc

This function is normally used inside view templates.  See render for example usage

Examples

//in app/views/test/index.ejs

<%=getElement("shared/common_commonlinks.ejs")%>
<%=getElement()%>

See

set

Controller.prototype.set = function set(prop,
val)

Sets a property on data

Parameters

propEither String property name to set, or JS object containing key/value pairs.  This will overwrite same-named properties but will preserver any other existing properties
valvalue to set

All top level properties of data become global variables in views and elements.

Returns val

Examples

function myAction(params){
//set single property
this.set("firstName","Bob")

//set multiple properties, preserves "firstName" set earlier
this.set({
lastName:"Dobb",
age:56
})

//you can also directly manipulate the data object
this.data.position="Slackmaster"
}
Controller.prototype.addLayout = function addLayout(layout)
adds a layout layer to this this request
Controller.prototype.setLayout = function setLayout(layout)
Sets a single layout layer to this this request, clearing any existing layouts
Controller.prototype.addFilter = function addFilter(filter,
options)
Adds an action filter
Controller.prototype.applyBehavior = function applyBehavior(name,
options)
loads a behavior and applies it to this object
Controller.prototype.getFilters = function addFilter(event,
action)
returns the filter array for the requested event and action.
Controller.prototype.addAction = function (name,
func,
overwrite)
Add an action function to this controller
Controller.prototype.getActions = function getActions()
returns an array of objects for each action that contain the action name and action function.
Controller.prototype.callAction = function callAction(action,
params)
Call an action as an inline function.
Controller.prototype.include = function include(action,
params)
Call an action and includes its output in the current request
Controller.prototype.init = function init(controllerName)
Initializes a controller instance
Controller.prototype.render = function render(options)
renders a view
Controller.prototype.renderContent = function renderBinary(data,
contentType,
filename)
Directly render content, bypassing default render
Controller.prototype.renderFile = function renderBinary(file)
Directly render a file to the browser, bypassing default render
Controller.prototype.getElement = function renderElement(element,
options)
renders an HTML template returns the content as a string
Controller.prototype.set = function set(prop,
val)
Sets a property on data
local request-specific data to be passed to views
In FlightPath, controllers (the C in MVC) are represented by the Controller class.
Data Modeling base class.
View Templates are .ejs templates used to render the result of an action.
Routes are URL patterns that can be mapped to controllers and actions.
function getController(controllerName)
factory function to find a a controller by name, instantiate it, initialize it, and return it.
Controller definitions can be stored in app/controllers/<controller_name>_controller.sjs or in a module:app/modules/module_name/controllers/<controller_name>_controller.sjs.
Myna FlightPath is a Model-View-Controller(MVC) web application framework inspired by CakePHP, CFWheels and Ruby On Rails.
A Javascript friendly proxy for java.io.File
A layout file is a specialized view (see: Views) that is intended to wrap content before sending it to the browser.
clear:function ()
Clears response buffer and returns the previous contents.
getContent:function ()
Returns current response buffer contents.
print:function (text)
Appends content to the response buffer.
Behaviors are functions that are applied to the current object.
$page metadata object
Myna.File.prototype.readBinary = function()
returns the contents of this file as a java byte[] array
printBinary:function(data,
contentType,
filename)
sends binary data to the browser
A MynaPath is a file URI String with certain automatic translations.
serveFile:function(file)
Efficiently serves a file on the filesystem.
An element file is a specialized view (see: Views) that is intended to contain a fragment of output (usually HTML) that can be used in a view