Layouts & Views Guide
- Layouts & Views Guide
- Introduction
- What is a Layout?
- What are Views?
- Rendering Data
- Implicit Declarations
- Setting Views For Rendering
- Rendering With No Layout
- Implicit Views
- Implicit View Helpers
- Rendering & Caching Views
- Purging Views
- Overriding Layouts
- Where are views/layouts rendered?
- Methods/Properties I can use
- Rendering Multiple Views
- Content Variable Views
- The ViewsExternalLocation Setting
- Rendering External Views
- Helper UDF's
- Viewlets
- Tips And Tricks
Introduction
ColdBox provides you with a very simple but flexible and powerful layout manager and renderer. The basic principles of rendering templates in ColdBox is that in your coldbox.xml you declare a DefaultLayout variable that points to the actual name of a template to use as your layout or container for all views to be rendered in. After that, it’s up to your handlers to determine what view to render and how to render it. So let's begin.
What is a Layout?
A layout is simply an HTML file that acts as your shell where views can be rendered in. The layouts can be composed of multiple views and one set view. The set view is the view that gets set by an event handler (This should have been covered in the event handlers guide). You can have as many layouts as you need in your application and they are super easy to override or assign to different parts of your application. Imagine switching content from a normal html layout to a PDF or Flash Paper layout with one line of code. How cool would that be? Well, it’s that cool with ColdBox. Another big benefit of layouts, is that you can see the whole picture of the layout, not the usual cfmodule calls where tables are broken, or divs are left open as the module wraps itself around content. The layout is a complete html document and you basically describe where views will be rendered. Much easier to build and simpler to maintain.

Layout Location
Layouts are located as per the conventions in the layouts folder and views are stored as per the conventions in the views folder.See Directory Structure for an in-depth guide on conventions and directory structure. So when you set a view to render or you render a view, you basically only give it its name and path starting from the views directory they are contained on. You also don't even give it an extension, the framework will append the .cfm for you.
|ApplicationRoot |----+ layouts (Layouts Directory)
Basic Layout
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" /> <title><cfoutput>#rc.title#</cfoutput></title> <!--- SES base ---> <cfoutput> <base href="#getSetting('htmlBaseURL')#"> </cfoutput> </head> <body> <cfoutput> <!--- Render a header view here and cache it for 30 minutes.---> #renderView(view='tags/header',cache=true,cacheTimeout='30')# <div id="content"> <table width="100%" border="0" cellspacing="0" cellpadding="10" > <tr style="border-bottom:1px solid ##eaeaea"> <td colspan="2" bgcolor="##0066CC"><span class="style1">#rc.title#</span></td> </tr> <tr> <td rowspan="2" align="center" valign="middle"> <!--- Use a plugin helper to render a UI messagebox ---> #getPlugin("messagebox").renderit()# <!--- Render the set view below: NO Arguments ---> #renderView()# </td> </tr> </table> </div> <!--- Render the footer and also cache it ---> #renderView(view='tags/footer',cache=true,cacheTimeout='30')# </cfoutput> </body> </html>
AJAX Layout
This layout can be used when rendering html snippets via AJAX. Instead of writing all of the following in the views, you can have a cool ajax layout to do it for you.
<cfsetting showdebugoutput="false"> <cfset event.showdebugpanel("false")> <cfset WriteOutput(getPlugin("messageBox").renderit())> <cfoutput>#renderView()#</cfoutput>
What are Views?
Views are also HTML/XML content that can be rendered inside of a layout or by themselves. They can be either rendered on demand or by being set by an event handler. They can also be rendered with no layout as we will see below. Views can also produce any type of content apart from HTML, like JSON/XML/WDDX etc, and we will cover this later in the guide.
Views Location
Views are located as per the conventions in the views folder. See Directory Structure for an in-depth guide on conventions and directory structure. Views can be rendered within layouts or by themselves.
|ApplicationRoot |----+ views (Views Directory)
Simple View
<cfoutput> <div align="center" class="mainDiv"> <div style="font-size:25px;font-weight:bold" align="left"> Transfer Simple Example </div> <div align="left" style="margin-top:10px;border:1px solid black;background:##fffff0;padding:10px"> This is a very simple Transfer example that gets a listing from the database and then you can add a new record and update a record. Please use the buttons below to interact with the sample. Please see the onAppInit method on the handler to see how the factory gets created and placed on the ColdBox cache. </div> <div style="margin-top:10px;" align="center"> <a class="action silver" href="#getSetting('sesBaseURL')#/users/dspHome" style="float:left"> <span>Home</span> </a> <a class="action" href="#getSetting('sesBaseURL')#/users/dspUsers" style="float:left"> <span>List Users</span> </a> <a class="action" href="#getSetting('sesBaseURL')#/users/dspAddUser" style="float:left"> <span>Add User</span> </a> </div> <div style="clear:both;margin-top:80px"> <hr> </div> </div> </cfoutput>
Rendering Data
You can learn much more about how to render data by reading the Ajax Guide. However, to give you a brief summary, the framework provides to you a utility method called renderData() located in the event object (request context). This method will provide you with the ability to render data back to the browser or to a remote caller without rendering or creating views. The framework will take care of marshalling (converting) the data for you and return it back. This is an incredible tool to use when doing AJAX or remote interactions from flex or air.
- renderData(type="JSON,WDDX,PLAIN",data,contentType)
The purpose of this method is for you to set the data to return, how to marshal or convert it and what type it is. The framework will then take care of everything for you, no need to abort the request or set a view or layout. The framework morphs into a remote framework. So not only can a call be done to the framework via the index.cfm but also by using the coldbox proxy. This method works with both calls. For more information on the coldbox proxy, please read the Coldbox Proxy Guide. A few samples:
<cffunction name="GetUsersXML" access="public" returntype="void"> <cfargument name="event" type="any" required="true"> <!--- Get a users query ---> <cfset var qUsers = getUserService().getUsers(); <!--- Render them ---> <cfset event.renderData(type="WDDX",data=qUsers)> </cffunction> <cffunction name="GetUsersJSON" access="public" returntype="void"> <cfargument name="event" type="any" required="true"> <!--- Get a users query ---> <cfset var qUsers = getUserService().getUsers(); <!--- Render them ---> <cfset event.renderData(type="JSON",data=qUsers)> </cffunction>
As you can see, it is very easy to render data back to the browser or caller. You can even choose plain and send html back if you wanted too.
Implicit Declarations
Now that we have seen what layouts and views are, where they are located and some samples, let’s dig deeper. There is a special section in the configuration file of an application just for layouts and views. This section is the <Layouts> section. Let's look at a sample declaration below:
<!--Declare Layouts for your application here--> <Layouts> <!--Declare the default layout, MANDATORY--> <DefaultLayout>Layout.Main.cfm</DefaultLayout> <DefaultView>Home</DefaultView> <!--Declare other layouts, with view assignments if needed, else do not write them--> <Layout file="Layout.Popup.cfm" name="popup"> <!--You can declare all the views that you want to appear with the above layout--> <View>vwTest</View> <View>vwMyView</View> <Folder>notices</Folder> </Layout> <Layout file="Layout.help.cfm" name="help"> <Folder>tags/help</Folder> </Layout> </Layouts>
- Declaring a Default Layout is mandatory
- Declaring a Default View is optional
- Declaring some layout/view and layouts/folder combinations is also optional but super handy.
With these settings you can declare right off the bat how your application will be rendered and save you some coding time. All you are doing is basic child-parent assignments. So let's start off with the Default Layout Element.
Default Layout
As you can see from the code above, the DefaultLayout element is mandatory and you define the name of the file to be your default layout. This file will need to be in the layouts directory under your application root as we covered before. That's it. This tells ColdBox that for any view renderings, this layout will be used. Thus, the call to render the default view will have to be inside of the layout of where exactly you want it to appear:
<div id="header"> my header here </div> <div id="content"> <!--- I want to render views here ---> <cfoutput>#renderView()#</cfoutput> </div> <div id="footer"> my footer here </div>
Default View
The Default View element is a very cool feature of the layout manager. This element allows you to declare the name of the view to render if the event handler does not set a view for rendering. This is a very cool feature, when prototyping applications.
Layout/View/Folder Assignments
Now we can get into the funky stuff. The Layout element allows you to declare a layout for a specific amount of views and folders. Let's start off with the sample below:
<!--Declare other layouts, with view assignments if needed, else do not write them--> <Layout file="Layout.Popup.cfm" name="popup"> <!--You can declare all the views that you want to appear with the above layout--> <View>vwTest</View> <View>vwMyView</View> <Folder>notices</Folder> </Layout> <Layout file="Layout.help.cfm" name="help"> <Folder>tags/help</Folder> </Layout>
The Layout element uses two attributes:
- @file : The file to use for the layout.
- @name : The unique short name of this layout.
In the sample, we declare the layout named popup and points to the file Layout.Popup.cfm. Next we can nest two types of child tags:
- <View> : The view to assign to this layout (no cfm extension)
- <Folder> : The folder and its children to assign to this layout.
This is cool, we can tell the framework that some views and some folders should be rendered within a specific layout. Wow, this opens the possibility of creating nested applications that need different rendering schemas! Is it that easy? Yes it is!!
So in the View element you declare the path to the view that you want rendered for this specific layout:
<Layout file="Layout.Popup.cfm" name="popup"> <View>vwTest</View> <!--- View that resides inside of a folder within the views convention ---> <View>blog/notices/notice</View> </Layout>
In this example, we can see that the last view element has a nested path. This will assign the file views/blog/notices/notice.cfm to the popup layout. This shows you that you can assign views located within folders.
So what about the folders element. Well, this is a very cool element, in which you can declare that all the views located inside of the declared folder will be rendered in that specific layout. Again, this is great for multiple applications:
<Layout file="Layout.Blog.cfm" name="popup"> <Folder>Blog</Folder> </Layout> <Layout file="Layout.Forum.cfm" name="forums"> <Folder>Forum</Folder> </Layout>
As you can see from the sample above, I create two layouts. The first layout is the blog layout that declares that any view that exists within the blog folder should be rendered within this layout. However, the second layout says that the forum folder of views will be declared in the forum layout. So anytime a view is rendered, these rules will be validated and a layout will be determined for you by the layout manager. Very nice indeed.
Setting Views For Rendering
Ok, so we have covered how to declare all this views and layouts and their defaults. What next? How do I get to render them. The first place to go to is the event handlers. Within an event handler method (action) we will use the event object to set a view to be rendered once execution finalizes. This is done by using the setView() method.
- setView(string name, [boolean nolayout], [boolean cache], [string cacheTimeout], [string cacheLastAccessTimeout])
<cffunction name="home" output="false" returntype="void"> <cfargument name="Event" type="coldbox.system.beans.requestContext"> <!--- Do all the controller logic HERE!! ---> <!--- Set the View To Display ---> <cfset Event.setView("vwHome")> </cffunction>
That's it, we use the setView() method to set a view to be rendered. Now the cool thing about this, is that we can override the view to be rendered anytime during the flow of the request. So the last event to execute the setView() method is the one that counts. Now we have learned two methods for layouts and views, so let’s recap:
- renderView() : Used to render the default view, called from a layout.
- event.setView( view ) : Used to set a view for rendering, called usually from an event handler.
Rendering With No Layout
So what happens if I DO NOT want the view to be rendered within a layout? Am I doomed? Of course not, just use the same method with some extra parameters:
<cfset event.setView(name='home',noLayout='true')>
That's it folks! You use the noLayout argument, another tool for our arsenal.
Implicit Views
You can also not even define a view if you want, ColdBox will then look for the view according to the executing event's syntax. So if the incoming event is called general.home and no view is explicitly defined, ColdBox will look for a view in the general folder called home.cfm. Simple as that. However, please read the important note below:
- event: general.home
- view: views/general/home.cfm
Implicit View Helpers
This is a nifty little feature that enables you to create nice helper templates on a per-view basis. If the framework detects the helper, it will inject it into the rendering view so you can use methods, properties or whatever. All you need to do is name the helper like so:
- View Name : home.cfm (The view to render)
- Helper Name : homeHelper.cfm (Has to exist where the view exists)
That's it. Just append Helper to the view name and there you go, the framework will use it as a helper for that view specifically.
Rendering & Caching Views
What about caching views? You said that a long time ago, how do I cache views? Well, again, we use our magic setView() or renderView method for it:
<cfset event.setView(name='home',noLayout='true',cache='true',cacheTimeout="60",cacheLastAccessTimeout="10")> //or <cfset event.setView(name='home',cache=true,cacheTimeout=30)> //RENDERINGS <cfoutput>#renderView(view='tags/footer',cache=true,cacheTimeout="60",cacheLastAccessTimeout="30")#</cfoutput>
We just told our rendering engine to render this view and cache it for 60 minutes and if the view does not get rendered by any event in the next 10 minutes, then just purge it.
Purging Views
So now that our views are cached, how do I purge them programmatically? Well, you use the renderer plugin or the ColdBox Cache Manager. The best practice is to use the cache manager, however, if you are purging simple views, then use the renderer plugin. We mentioned before that the renderer plugin is used by the framework to render layouts and views. It is also used to cache and purge views. We will learn more about what the renderer plugin does for us as this guide continues. So let's just digest a new method.
<cfset getPlugin("renderer").purgeView('home')>
That's it! We call the renderer plugin via the getPlugin() method and we then call the purgeView() method on it.
- purgeView( name ) : This will purge a named view.
Let's look at some of the cache manager methods:
- clearView(string viewSnippet, [boolean async=true]) : Used to clear a view from the cache by using a snippet and can be run asynchronously.
- clearAllViews([boolean async=true]) : Can clear ALL cached views in one shot and can be run asynchronously.
Overriding Layouts
Ok, now that we have started to get funky, let’s keep going. How can I change the layout on the fly for a specific view? Very easily, using yet another new method from the event object, called setLayout()
- setLayout( name ) : Set the layout for rendering, do not append a .cfm extension.
This is great, so we can change the way views are rendered on the fly programmatically. We can switch the content to a PDF in an instant. So let's do that
<cffunction name="home" output="false" returntype="void"> <cfargument name="Event" type="coldbox.system.beans.requestContext"> <!--- set the pdf layout if a 'displaypdf' variable exists ---> <cfif event.valueExists('displayPDF')> <cfset Event.setLayout('Layout.PDF')> </cfif> <!--- Set the View To Display ---> <cfset Event.setView("vwHome")> </cffunction>
WOW!! That easy! We check if a variable displayPDF is sent in and if it is, we override the layout with the event.setLayout() method. Another interesting technique is using some of the implicit execution points of a handler: preHandler() & postHandler(). You can use these implicit events to determine a layout for an entire event handler.
<cffunction name="preHandler" output="false" returntype="void"> <cfargument name="Event" type="coldbox.system.beans.requestContext"> <!--- Set a layout before anything executes ---> <cfset Event.setLayout('Layout.PDF')> </cffunction> <cffunction name="postHandler" output="false" returntype="void"> <cfargument name="Event" type="coldbox.system.beans.requestContext"> <!--- Set a layout after an event executes or even check for data in the rc in order to change the layout. ---> <cfset Event.setLayout('Layout.PDF')> </cffunction>
So let's recap our arsenal:
- renderView() : Used to render the default view, called from a layout.
- event.setView(string name, [boolean nolayout], [boolean cache], [string cacheTimeout], [string cacheLastAccessTimeout]) : Used to set views.
- event.setLayout(string name) : Used to override layouts.
- getPlugin("renderer").purgeView(name)
- getColdboxOCM().clearView(string viewSnippet, [boolean async=true]) : Used to clear Views by using snippets
- getColdboxOCM().clearAllViews() : Clear all views
Where are views/layouts rendered?
Now let’s cover the renderer plugin a little bit more. We have now understood how to set views for rendering, how to override layouts, how to cache views and how views and layouts are interconnected. So the following are some pointers about all layout/view renderings:
- All layouts and views are rendered in their own context within the renderer plugin.
- All layouts and views have the following variables available to them:
- event : The event object (request context)
- rc : The rc scope is a reference to the request collection inside of the event object.
- controller : A reference to the coldbox controller object
- instance' : the plugins' instance scope
- You can call any method or framework life cycle method from within views. Look at the framework super type class in the <a href="http://www.coldboxframework.com">CFC API</a>.
Methods/Properties I can use
So let's list some of the methods that this renderer plugin provides you with and you can just use from within your layouts and views. Why shouldn't I call the renderer plugin directly? Well, technically you still can, but why call it, if you are already in it? Ok Ok, you just confused me more. What do you mean I am already in it? Well, your views and layouts code actually execute within the renderer plugin, they are part of it. So you can call any method in the renderer plugin just like any other component. FUNKY!! (The renderer plugin is thread safe, it means, that each rendering is a different context)
- getappMapping() : Get the appMapping variable
- getlayoutsConvention() : Get the layouts convention directory
- getviewsConvention() : Get the views convention directory
- purgeView(string view) : Purge a named view
- renderExternalView() : Renders a view outside of the Application root.
- renderView() : Renders a set view or a named view.
- includeUDF()' : Ability to mixin UDF's into the layouts/views
Finally, again, remember that you have access to any of the methods in the base plugin.cfc class, so please refer to the <a href="http://www.coldboxframework.com">CFC API</a> for more information.
Rendering Multiple Views
What if I want to organize some of my views into sections and render more than one at a time. Can I do this? Yes, just use the same renderView() method but with the name parameter set explicitly. We covered this before, but this is just another reminder.
<cfoutput> #renderView('tags/header')# <div id="content"> #renderView()# </div> #renderView('tags/footer')# </cfoutput>
We just rendered several views in a layout. Can I do the same within a view? Of course. You can use it anywhere you like, inside of the other views, wherever.
Content Variable Views
Can I use variables as content variables? Yes!! Ohh by the way, what is a content variable?
A content variable is a variable that contains HTML/XML or any kind of visual content
You can easily create content variables by using the renderer plugin within your event handlers, plugins, interceptors, etc:
<cffunction name="home" output="false" returntype="void"> <cfargument name="Event" type="coldbox.system.beans.requestContext"> <cfset var rc = event.getCollection()> <!--- set the pdf layout if a 'displaypdf' variable exists ---> <cfif event.valueExists('displayPDF')> <cfset Event.setLayout('Layout.PDF')> </cfif> <!--- Render & set a content variable ---> <cfset event.setValue('myContentVariable', getPlugin('renderer').renderView('tags/sideColumn') )> <!--- Same as above in shorthand syntax ---> <cfset rc.myContentVariable = getPlugin('renderer').renderView('tags/sideColumn')> <!--- Set the View To Display ---> <cfset Event.setView("vwHome")> </cffunction>
That easy, just by using a combination of the request collection and the renderer plugin you have created your first content variable. So how do I render it?
<!--- The following is in a layout. ---> <div id="content"> <div id="leftColumn"> <cfoutput>#rc.myContentVariable#</cfoutput> </div> <div id="mainView"> <cfoutput>#renderView()#</cfoutput> </div> </div>
Another example, is what if we do not know if the content variable will actually exist? How can we do this? Well, we use the event object for this and its magic getValue() method.
<!--- The following is in a layout. ---> <div id="content"> <div id="leftColumn"> <cfoutput>#event.getValue('myContentVariable','')#</cfoutput> </div> <div id="mainView"> <cfoutput>#renderView()#</cfoutput> </div> </div>
The second parameter to the getValue method is a default value if the variable does not exist. So now, if no content variable exists, a blank will be rendered.
The ViewsExternalLocation Setting
The cool thing about ColdBox is its extensibility. Another area of extensibility is the ability to define another location of where to look for views to be rendered. This is easily setup by using the ViewsExternalLocation setting. The value of this setting is the prefix of where the views are located. However, please remember that this value will be used by using the cfinclude tag. So make sure it’s a valid path or cf mapping.
<Setting name="ViewsExternalLocation" value="modules/views" />
As you can see from above, my external location is a modules/views subfolder. So how are these views used? Well, like normal views, you do not explicitly tell it it’s an external location, but you just set and render as any normal view. The framework will then look for that view in your local views folder first (or whatever convention you have) and then if it does not find it there, it will look in this location, if defined. This is a great way to enhance your applications or modularize them.
Rendering External Views
So what if I want to render a view outside of my application without using the setting explained above? Well, you use the renderExternalView() method in the renderer plugin.
- renderExternalView(string view, [boolean cache], [string cacheTimeout], [string cacheLastAccessTimeout])
<cfoutput>#renderExternalView('/myViewsMapping/tags/footer')#</cfoutput>
Helper UDF's
ColdBox provides you with a way to actually inject your layouts/views with custom UDF's, so they can act as helpers. This is called mixin methods and can be done via the includeUDF() method provided to the renderer plugin or via the UDFLibrary setting in your coldbox.xml. The method is a provided way for you to dynamically load UDF's into your views/layouts at runtime.
<Setting name="UDFLibrary" value="includes/viewUDFs.cfm" />
<cfset includeUDF('includes/helpers/viewHelper.cfm')> <!---My View below --->
- The includeUDF method call will find the template and inject it to the layout/view combination
- The UDFLibrary setting injects the UDF's in the template to the handlers/layouts and views.
Viewlets
The final chapter in this guide is to explain what I consider as a ColdBox viewlet. A viewlet to me is a self sufficient view. A view that can live on its own, its data is pre-fetched and can just be renderer as is. This view is also unique because it has knowledge about which events to announce before executing its content. What in the world is this? Well, imagine a portal, in which each section of the portal is self-sufficient, with controls and data. You don't want to call all the handlers for this data for every single piece. It's not efficient, you need to create a separation. Well, a viewlet is such a separation that provides you with the ability to announce events or interceptions from the view itself. You can simulate this to be a page controller.
As best practice, I usually create a folder within my views convention called viewlets this way I know that only viewlets will go there.
So the sample below is a viewlet that renders a query, but the query needs to be set by an event. So this viewlet will look like so:
viewlets/myViewlet.cfm:
<!--- Execute Event ---> <cfset runEvent(event='viewlets.prepareViewlet1',private=true)> <!--- Or announce an interception ---> <cfset announceInterception('prepareMyViewlet')> <!--- Output Query ---> <cfoutput query="rc.MyQuery"> #currentrow#. #title# </cfouput>
Event Handler(viewlets.prepareViewlet1):
<cffunction name="prepareViewlet1" output="false" returntype="void" access="private"> <cfargument name="Event" type="coldbox.system.beans.requestContext"> <cfset var rc = event.getCollection()> <cfset rc.myQuery = getPlugin("ioc").getBean('myService').getQuery()> </cffunction>
As you can see from the code, the event, just retrieves some query from a service and places it on the request collection. The viewlet itself, just calls the event it’s bound to and then displays data. You can also announce an interception instead of running events if you would like to implement a listener approach. Then the viewlet can be rendered.
<!--- The following is in a layout. ---> <div id="content"> <div id="leftColumn"> <cfoutput>#event.getValue('myContentVariable','')#</cfoutput> </div> <div id="mainView"> <cfoutput> #renderView()# <!--- I want the viewlet here, cache it for 10 min ---> #renderView('viewlets/myViewlet',true,10)# </cfoutput> </div> </div>
And there you go, you have just created a self-sufficient view that can be reused in any context. Not only that, but that viewlet can be cached if needed by adding the cache parameters to it.
Tips And Tricks
Some of the methods below will help you when dealing with views and their renderings.
I don't want to render anything
What if I don't want to render anything, just process. Then use the noRender() method, no need to abort or anything. This tells the framework to stop gracefully.
<cfset event.noRender()>
I don't want a coldbox debugging panel attached to this view
<cfset event.showdebugpanel("false")>
Is this a ColdBox proxy request
<cfset event.isProxyRequest()>
Am I in an ses application
<cfset event.isSES()>
What is the current layout?
<cfset event.getCurrentLayout()>
What is the current view
<cfset event.getCurrentView()>
PLEASE READ THE CFC DOCS!! TONS OF INFORMATION THERE TOO!'''
