Layouts & Views Guide
- Layouts & Views Guide
- Introduction
- What is a Layout?
- What are Views?
- Implicit Declarations
- Setting Views For Rendering
- Rendering With No Layout
- Overriding Layouts
- Where are views/layouts rendered?
- Methods/Properties I can use
- Rendering Multiple Views
- Rendering External Views
- Content Variable Views
- Helper UDF's
- Viewlets
- Tips And Tricks
- Conclusion
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.
What is a Layout?
A layout is simply an HTML/XML file that acts as your shell where views can be rendering 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, its that cool with ColdBox.

Layout Location
Layouts are located as per the conventions in the layouts folder. See Directory Structure for an in-depth guide on conventions and directory structure.
|ApplicationRoot |----+ layouts (Layouts Directory)
Sample Code
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 here ---> #renderView('tags/header')# <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> </cfoutput> <div id="footer"> <p> </p> <p align="center"><a href="http://www.luismajano.com/projects/coldbox"><img src="../../images/poweredby.png" border="0"></a></p> </div> </body> </html>
AJAX Layout
<cfsetting showdebugoutput="false"> <cfset event.showdebugpanel("false")> <cfset WriteOutput(getPlugin("messageBox").renderit())> <cfset WriteOutput(renderView())>
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.
|ApplicationRoot |----+ views (Views Directory)
Sample Code
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 get's 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>
XML Data Proxy View
<cfcontent reset="true" type="text/xml"> <cfoutput>#rc.data#</cfoutput>
Implicit Declarations
Now that we have seen what layouts and views are, where they are located and some samples, lets 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.
<!--Declare Layouts for your application here--> <Layouts> <!--Declare the default layout, MANDATORY--> <DefaultLayout>Layout.Main.cfm</DefaultLayout> <DefaultView>Home</DefaultHome> <!--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> </Layouts>
- Declaring a Default Layout is mandatory
- Declaring a Default View is optional
- Declaring some layout/view and layouts/folder combinations are 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 specifc 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>
The Layout element uses two attributes:
- file : The file to use for the layout.
- name : The unique shortname 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. (Can be nested)
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="popup"> <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, this 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 we will use the event object to set a view to be rendered once execution finalizes.
<cffunction name="home" output="false" returntype="void"> <cfargument name="Event" type="coldbox.system.beans.requestContext"> <!--- 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 lets 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.
Caching Parameters
What about caching views? You said that a long time ago, how do I cache views? Well, again, we use our magic setView() 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)>
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.
So let's now review our full setView() arsenal:
- setView(string name, [boolean nolayout], [boolean cache], [string cacheTimeout], [string cacheLastAccessTimeout])
| name | The name of the view to render, no cfm extension |
| noLayout | Boolean flag that tells the framework to render a layout for the view or not |
| cache | Boolean flag that tells the framework to cache this view |
| cacheTimeout | In minutes, how long this view will be cache for |
| cacheLastAccessTimeout | In minutes, how long should this view be checked if it has been rendered. Else, purge it. |
Purging Parameters
So now that our views are cached, how do I purged them explicitly? Well, you 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.
Overriding Layouts
Ok, now that we have started to get funky, lets keep going. How can I change the view on the fly for a specific layout? 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. 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. 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.
Where are views/layouts rendered?
Now lets 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
- rc : The rc scope is a reference to the request collection inside of the event object.
- You can call any method or framework life cycle method from within views.
Methods/Properties I can use
So let's list some of the methods that this renderer plugin provides for you that you can call with calling the plugin. Why shouldn't I call the plugin? 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!!
- getappMapping() : Get appMapping
- getlayoutsConvention() : Get the layouts convention
- getviewsConvention() : Get the views convention
- purgeView(string view) : Purge a named view
- renderExternalView(string view) : Renders a view outside of the Application root.
- renderView([string view]) : Renders a set view or a named view.
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.
<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.
Rendering External Views
So what if I want to render a view outside of my application? Well, you use the renderExternalView() method in the plugin.
<cfoutput>#renderExternalView('/myViewsMapping/tags/footer.cfm')#</cfoutput> <!--- Render a CSS File ---> <style> <cfoutput>#renderExternalView('/myExternalLocation/styles/main.css')#</cfoutput> </style>
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:
<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> <!--- Get 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. COOOL!!!
Helper UDF's
ColdBox provides you with a way to actually inject your layouts/views with custom UDF's. 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 handler.
Viewlets
The final chapter in this guide is to explain what I consider as a 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. 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. Its not efficient, you need to create a separation. Well, a viewlet is such a separation and all it comes down to, its that its a view that calls an event for data preparation. You can simulate this to a page controller.
As best practice, I usually create a folder within my views convention called viewlets that 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:
<!--- Call event handler ---> <cfset runEvent('viewlets.prepareViewlet1')> <cfoutput query="rc.MyQuery"> #currentrow#. #title# </cfouput>
Event Handler (viewlets.prepareViewlet1):
<cffunction name="prepareViewlet1" output="false" returntype="void"> <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 its bound to and then displays data. As you can see, the view is bound to the event, but the entire viewlet is a separate object on its own. It can just 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 ---> #renderView('viewlets/myViewlet')# </cfoutput> </div> </div>
And there you go, you have just created a self-sufficient view that can be reused in any context.
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
Then call the following within an event:
<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()>
What is the current layout?
<cfset event.getCurrentLayout()>
What is the current view
<cfset event.getCurrentView()>
Conclusion
As you can see from this guide, there are several ways for you to use the layout manager for any type of purpose. There are several tips and tricks you can use and they can be found throughout all the guides. So please read them and put them in practice.
