layouts-views
Layouts & Views Guide
Introduction
ColdBox provides you with a very simple but flexible and powerful layout manager and content renderer. You no longer need to create module tags or convoluted broken up HTML anymore. You can concentrate on the big picture and create as many layouts as your application needs. Then you can programmatically change rendering schemas (or skinning) and also create composite views. In this guide we will explore the different rendering mechanisms that ColdBox offers and also how to utilize them. As you know, event handlers are our controller layer in ColdBox and we will explore how these objects can interact with the user in order to render content, whether HTML, JSON, XML or any type of rendering data.
Response Types
The event handlers will be the objects in question that will be responding to user requests and they have a few choices when it comes down to rendering content back:
- Set a view to be rendered back to the user (with or without a layout)
- Render data in multiple view formats: JSON, XML, WDDX, HTML, TEXT or CUSTOM
- Render nothing
- Do an HTTP redirect to another event (part of our event handler guide via setNextEvent())
Conventions
Let's do a recap of our conventions for layouts and view locations:
+ application + layouts + views
All your layouts will go in the layouts folder and all your views will go in the views folder.
Also remember that ColdBox allows you to change your conventions via the application configuration CFC.
ColdBox Renderer
It is imperative to know who does the rendering in ColdBox and that is the Renderer core plugin that you can see from our diagram above. As you can tell from the diagram, it includes your layouts and/or views into itself in order to render out content. So by this association and inheritance all layouts and views have some variables and methods at their disposal since they get absorbed into the plugin. You can visit the API docs to learn about all the Renderer Plugin methods. All of the following property members exist in all layouts and views rendered by the Renderer:
| Property | Description |
|---|---|
| event | A reference to the Request Context |
| rc | A reference to the request collection inside of the request context (For convenience) |
| prc | A reference to the private request collection inside of the request context (For convenience) |
| html | A reference to the HTML Helper plugin (coldbox.system.plugins.HTMLHelper) that can help you build interactive HTML |
| cacheBox | A reference to the CacheBox framework factory (coldbox.system.cache.CacheFactory) |
| controller | A reference to the application's ColdBox Controller (coldbox.system.web.Controller) |
| flash | A reference to the current configured Flash Object Implementation that inherits from the AbstractFlashScope AbstractFlashScope (derived coldbox.system.web.flash.AbstractFlashScope) |
| logBox | The reference to the LogBox library (coldbox.system.logging.LogBox) |
| log | A pre-configured LogBox Logger object for this specific class object (coldbox.system.logging.Logger) |
| wirebox | A reference to the WireBox object factory (coldbox.system.ioc.Injector) |
As you can see, all views and layouts have direct reference to the request collections so it makes it incredibly easy to get and put data into it. Also, remember that the Renderer inherits from the base ColdBox Plugin class which in turn inherits from the FrameworkSuperType class. So all methods are at your disposal if needed.
Do not put any type of business logic in layouts and views, it does not belong there!
Rendering Views
Views are 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. Views can also produce any type of content apart from HTML like JSON/XML/WDDX via our view renderer that we will discover also. So get ready for some rendering goodness!
Setting Views For Rendering
Usually, event handlers are the objects in charge of setting views for rendering. However, ANY object that has access to the request context object can do this also. This is done by using the setView() method in the request context object. Let's see the signature of this magnificent method:
setView([string view], [boolean nolayout='false'], [boolean cache='false'], [string cacheTimeout=''], [string cacheLastAccessTimeout=''], [string cacheSuffix=''], [string layout], [string module])
Handler Code:
component name="general"{ function index(event,rc,prc){ // call some model for data and put into the request collection prc.myQuery = getModel('MyService').getData(); // set the view for rendering event.setView(view="general/index"); } }
View Location (view="general/index"):
/application
/views
/general
+index.cfm
That's it, we use the setView() method to set the view general/index.cfm 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. Also notice a few things:
- No .cfm extension is needed.
- You can traverse directories by using / like normal cfinclude notation.
- The view can exist in the conventions directory views or in your configured external locations.
- You did not specify a layout for the view, so the application's default layout will be used, we will cover this later on.
Let's look at the view code:
<cfoutput> <h1>My Cool Data</h1> #html.table(data=prc.myQuery)# </cfoutput>
I am using our cool HTML Helper plugin that is smart enough to render tables, data, HTML 5 elements etc and even bind to ColdFusion ORM entities. All layouts/views have access to the HTML helper.
Implicit Views
You can also omit the explicit event.setView() 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.index and no view is explicitly defined in your handler, ColdBox will look for a view in the general folder called index.cfm. That is why we recommend trying to match event resolution to view resolution even if you use or not implicit views. This feature is more for conventions purists than anything else. However, we do recommend as best practice to use explicitly declare the view to be rendered when working with team environments. Below is the change in code to use implicit views, basically remove the setView() method call.
component name="general"{ function index(event,rc,prc){ // call some model for data and put into the request collection prc.myQuery = getModel('MyService').getData(); } }
Important: If using implicit views, please note that the name of the view will ALWAYS be in lower case. So please be aware of this limitation. I would suggest creating SES URL Mappings with explicit event declarations so case and location can be controlled. When using implicit views you will also loose fine rendering control.
Case Sensitivity
The ColdBox rendering engine can also be tweaked to use case-insensitive or sensitive implicit views by creating a setting called: caseSensitiveImplicitViews in your configuration file. The default is to turn all implicit views to lower case, so the value is always false.
settings = {
caseSensitiveImplicitViews = true
};
Views 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 or event.noLayout():
component name="general"{ function index(event,rc,prc){ // call some model for data and put into the request collection prc.myQuery = getModel('MyService').getData(); // set the view for rendering event.setView(view="general/index",noLayout=true); } function index(event,rc,prc){ // call some model for data and put into the request collection prc.myQuery = getModel('MyService').getData(); // set the view for rendering event.setView(view="general/index").noLayout(); } }
That's it folks! You use the noLayout=true argument or the noLayout() method.
Views With Specific Layout
If you need the view to be rendered in a specific layout, then use the layout argument:
component name="general"{ function index(event,rc,prc){ // call some model for data and put into the request collection prc.myQuery = getModel('MyService').getData(); // set the view for rendering event.setView(view="general/index",layout="Ajax"); } }
The view will now be rendered into the Ajax layout instead of the default one.
Views From A Module
If you need the set view to be rendered from a specific ColdBox Module then use the module argument alongside any other argument combination:
component name="general"{ function index(event,rc,prc){ // call some model for data and put into the request collection prc.myQuery = getModel('MyService').getData(); // set the view for rendering event.setView(view="general/index",module="shared-views"); } }
By using the module argument, you are explicitely telling the ColdBox renderer to look for the view inside of the passed module name.
View Caching
You can also pass in the caching arguments below and your view will be rendered once and then cached for further renderings. Every ColdBox application has two active cache regions by default: default and template. All view and event caching renderings go into the template cache.
| Argument | Type | Required | Default | t I just call methods on the member as if I was looping (which we are for you). But you will also see two little variables here:
#renderView(view="home/news", collection=prc.news, collectionStartRow=11, collectionMaxRows=20)#
View HelpersThis is a nifty little feature that enables you to create nice helper templates on a per-view or per-folder 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 follow a set of conventions. Let's say we have a view in the following location:
/views
/general
+home.cfm
Then we can create the following templates
/views
/general
+home.cfm
+homeHelper.cfm
+generalHelper.cfm
homeHelper.cfm:
<cfscript> // facade to get i18n resource function $r(){ return getResource(argumentCollection=arguments); } // cool formatted date function function today(){ return dateFormat(now(),"full"); } </cfscript>That's it. Just append Helper to the view or folder name and there you go, the framework will use it as a helper for that view specifically. What can you put in these helper templates:
View EventsAll rendered views have associated events that are announced whenever the view is rendered. These are great ways for you to be able to intercept when views are rendered and transform them, append to them, or even remove content from them in a completely decoupled approach. The way to listen for events in ColdBox is to write Interceptors, which are essential simple CFC's that by convention have a method that is the same name as the event they would like to listen to. Each event has the capability to receive a structure of information wich you can alter, append or remove from. Once you write these Interceptors you can either register them in your Configuration File or programmatically.
You can disable the view events on a per-rendering basis by passing the prePostExempt argument as true when calling renderView() methods.
Sample InterceptorHere is a sample interceptor that trims any content before it is renderer:
component{
function postViewRender(event,interceptData){
interceptData.renderedView = trim( interceptData.renderedView );
}
}
Of course, I am pretty sure you will be more creative than that!
LayoutsA layout is simply an HTML file that acts as your shell where views can be rendered in and exists in the layouts folder of your application. The layouts can be composed of multiple views and one main view. The main view is the view that gets set in the request collection during a request via event.setView() usually in your handlers or interceptors. 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 mobile 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.![]() 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"> <---< Use a plugin helper to render a UI messagebox ---> #getPlugin("messagebox").renderit()# <---< Render the set view below: NO Arguments ---> #renderView()# </div> <---< Render the footer and also cache it ---> #renderView(view='tags/footer',cache=true,cacheTimeout='30')# </cfoutput> </body> </html> Nested LayoutsYou can also wrap layouts within other layouts and get incredible reusability. This is accomplished by using a method from our glorious Renderer plugin: renderLayout(). As always, refer to the CFC API for the latest method arguments and capabilities.renderLayout([any layout], [any view], [any module], [any args])So if I wanted to wrap my basic layout, we just showed you (basic.cfm), in a PDF wrapper layout (pdf.cfm) I could do the following: <cfdocument pagetype="letter" format="pdf"> <--- Header ---> <cfdocumentitem type="header"> <cfoutput> <div> #dateformat(now(),"MMM DD, YYYY")# at #timeFormat(now(),"full")# </div> </cfoutput> </cfdocumentitem> <--- Footer ---> <cfdocumentitem type="footer"> <cfoutput> <div> Page #cfdocument.currentpagenumber# of #cfdocument.totalpagecount# </div> </cfoutput> </cfdocumentitem> <--- Main Content via nested layout ---> #renderLayout(layout="basic")# </cfdocument>That's it! The renderLayout() method is extremely power as it can allow you to not only nest layouts but actually render a-la-carte layout/view combinations also. Default LayoutAll ColdBox applications give you the capability of choosing a default layout that can be used to render all your views in. This is done by choosing that default layout in your Configuration CFC in the layoutSettings structure://Layout Settings layoutSettings = { defaultLayout = "basic.cfm" }With this little configuration snippet, all set views or main views that do not define a layout will use the default one by convention. Default ViewThe 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.//Layout Settings layoutSettings = { defaultLayout = "basic.cfm", defaultView = "noview.cfm" } Layout HelpersThis is a nifty little feature that enables you to create nice helper templates on a per-layout or per-layout-folder basis. If the framework detects the helper, it will inject it into the rendering layout so you can use methods, properties or whatever. All you need to do is follow a set of conventions. Let's say we have a layout in the following location:
/layouts
/general
+main.cfm
Then we can create the following templates
/layouts
/general
+main.cfm
+mainHelper.cfm
+generalHelper.cfm
That's it. Just append Helper to the layout or folder name and there you go, the framework will use it as a helper for that layout specifically.
Important : Please note that layout helpers will be inheritenly available to any view rendered inside of the layout.
Overriding LayoutsOk, 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()event.setLayout( name )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 function home(event,rc,prc){ if( event.valueExists('print') ){ event.setLayout('layout.PDF'); } // logic here // set view event.setView('general/home'); }WOW |
That easy! We check if a variable print 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.
function preHandler(event,action,eventArguments){ event.setLayout('Layout.PDF'); } function postHandler(event,action,eventArguments){ event.setLayout('Layout.PDF'); }But you can even get more creative and decide to either create a preProcess Interceptor or a request start handler and listen for certain environment changes and switch layouts. You can then completely take control of how views will be rendered according to maybe browser type, mobile usage, etc. function requestStartHandler(event,rc,prc){ if( mobileLayout ){ event.setLayout('Layout.Mobile'); } } Implicit Layout/View DeclarationsNow 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 and you can find much more in detail information in the Configuration CFC guide. Let's look at a sample declaration below://Register Layouts layouts = [ { name = "login", file = "Layout.tester.cfm", views = "vwLogin,test", folders = "tags,pdf/single" } ];This setting allows you to implicitly define layout to view/folder assignments without the need of programmatically doing it. This is a time saver and a nice way to pre-define how certain views will be rendered. Let's see more examples: //Register Layouts layouts = [ { name = "Popup", file = "Layout.Popup.cfm", views = "login,test", folders = "^tags,pdf/single" }, { name = "help", file = "layout.help.cfm", folders = "^help" } ];In the sample, we declare the layout named popup and points to the file Layout.Popup.cfm. We can then assign it to views or folders:
Layouts From A ModuleIf you need the set layout to be rendered from a specific ColdBox Module then use the module argument alongside any other argument combination:component name="general"{ function index(event,rc,prc){ // call some model for data and put into the request collection prc.myQuery = getModel('MyService').getData(); // set the view for rendering event.setLayout(layout="admin",module="contentbox"); } }By using the module argument, you are explicitely telling the ColdBox renderer to look for the layout inside of the passed module layout's convention. Layout EventsAll rendered layouts have associated events that are announced whenever the layout is rendered. These are great ways for you to be able to intercept when layouts are rendered and transform them, append to them, or even remove content from them in a completely decoupled approach. The way to listen for events in ColdBox is to write Interceptors, which are essential simple CFC's that by convention have a method that is the same name as the event they would like to listen to. Each event has the capability to receive a structure of information wich you can alter, append or remove from. Once you write these Interceptors you can either register them in your Configuration File or programmatically.
You can disable the layout events on a per-rendering basis by passing the prePostExempt argument as true when calling renderLayout() methods.
Rendering DataYou can learn much more about how to render data by reading the Ajax Guide. ColdBox provides you with a utility method called renderData() located in the event object (request context) that can be used from your event handlers. 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 or when building RESTful web services. You can also do your own conversions if you like, but it would be your responsibility to format the data correctly, set the correct headers, econding and content type. We call these view transformers and can be done very easily!public any renderData(string type='HTML', any data, string contentType='', [string encoding='utf-8'], [numeric statusCode='200'], [string statusText=''], [string location=''], [string jsonCase='lower'], [string jsonQueryFormat='query'], [boolean jsonAsText='false'], [string xmlColumnList=''], [boolean xmlUseCDATA='false'], [string xmlListDelimiter=','], [string xmlRootName=''])Out of the box ColdBox can marshall data (structs,queries,arrays,complex or even ORM entities) into the following output formats:
Global Parameters
JSON Parameters
XML Parameters
// xml marshalling function getUsersXML(event,rc,prc){ var qUsers = getUserService().getUsers(); event.renderData(type="XML",data=qUsers); } //json marshalling function getUsersJSON(event,rc,prc){ var qUsers = getUserService().getUsers(); event.renderData(type="json",data=qUsers); } //jsonp marshalling function getUsersJSON(event,rc,prc){ var qUsers = getUserService().getUsers(); event.renderData(type="jsonp",data=qUsers,jsonCallback="myCallback"); } // restful handler function list(event,rc,prc){ event.paramValue("format","html"); rc.data = userService.list(); switch(rc.format){ case "json": "jsont" : "xml" { event.renderData(type=rc.format,data=rc.data); break; } default: { event.setView("users/list"); } } } // simple tests function data(event,rc,prc){ var data = { name = "ColdBox", awesome = true, ratings = [5,5,4,3] }; event.renderData(data=data,type="json"); }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. Custom Data ConversionYou can do custom data conversion by convention when marshalling CFCs. If you pass in a CFC as the data argument and that CFC has a method called $renderdata(), then the marshalling utility will call that function for you instead of using the internal JSON/XML/WDDX marshalling utilities. You can pass in the custom content type for encoding as well: The call to renderdata from your handlers:// get an instance of your custom converter myConverter = getModel("MyConverter") // put some data in it myConverter.setData( data ); // marshall it out according to your conversions and the content type it supports event.renderData(data= myConverter, contentType=myConverter.getContentType());The CFC converter: component accessors="true"{ property name="data" type="mytype"; property name="contentType"; function init(){ setContentType("text"); return this; } // The magical rendering function $renderdata(){ var d = { n = data.getName(), a = data.getAge(), c = data.getCoo(), today = now() }; return d.toString(); } }In this approach your $renderdata() function can be much more customizable than our internal serializers. Just remember to use the right contentType argument so the browser knows what to do with it. Helper UDF'sColdBox 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 configuration file. The method is a provided way for you to dynamically load UDF's into your views/layouts at runtime and the UDFLibrary setting is a convention that acts globally on all layouts, views and event handlers.coldbox.udfLibrary = 'includes/helpers/applicationHelper.cfm'; <cfset includeUDF('includes/helpers/viewHelper.cfm')> <---MMy View below --->
Warning: If you try to inject a method that already exists, the call will fail and ColdFusion will throw an exception. Also, try not to abuse mixins, if you have too many consider refactoring into model objects or plugins.
Viewlets (Portable Events)The final chapter in this guide is to explain what we consider as a ColdBox viewlet or portable events. A viewlet is a self sufficient view or a widget that can live on its own, its data is pre-fetched and can just be renderer anywhere in your system. 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 of content. It's not efficient, you need to create a separation. Well, a viewlet is such a separation that provides you with the ability to create sustainable events or viewlets. So how do we achieve this?
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| cache | boolean | false | false | Cache the view to be rendered | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cacheTimeout | numeric | false | (provider default) | The timeout in minutes or whatever the cache provider defines | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cacheLastAccessTimeout | numeric | false | (provider default) | The idle timeout in minutes or whatever the cache provider defines | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cacheSuffix | string | false | --- | Adds a suffix key to the cached key. Used for providing uniqueness to the cacheable entry |
component name="general"{ function index(event,rc,prc){ // call some model for data and put into the request collection prc.myQuery = getModel('MyService').getData(); // view with caching parameters event.setView(view="general/index",cache=true,cacheTimeout=60,cacheLastAccessTimeout=15,cacheSuffix=getfwLocale()); } }
Purging Views
So now that our views are cached, how do I purge them programmatically? Well, you need to talk to the template cache provider.
// get a reference to the template cache cache = cachebox.getCache('template'); // or cache = getColdBoxOCM('template');
Then we can perform several operations on views:
- clearView(string viewSnippet): Used to clear a view from the cache by using a snippet matched according to name + cache suffix.
- clearMultiView(any viewSnippets): Clear using a list or array of view snippets.
- clearAllViews([boolean async=true]) : Can clear ALL cached views in one shot and can be run asynchronously.
cachebox.getCache('template').clearView('general/index'); cachebox.getCache('template').clearAllViews(async=true); cachebox.getCache('template').clearMultiView('general/index','index','home');
Handler Return Rendering
Handlers can also return content by simply returning a simple string from the action method. How that string is built is up to you, it can be a simple string, a call from a model CFC, or inline rendering that we will see below.
component name="general"{ function index(event,rc,prc){ return "<h1>Hello from my handler today at :#now()#</h1>"; } }
That's it, just return the simple content and it will sent to the output buffer. This technique is great for producing viewlets/widgets or self-sustainable events, where you can call and render events from within anywhere in ColdBox via the runEvent() method. Please refer to the viewlets section in this guide.
Render Nothing
You can also tell the renderer to not render back anything to the user by using the event.noRender() method.. Maybe you just took some input and need to gracefully shutdown the request.
component name="general"{ function saveData(event,rc,prc){ // do your work here ….. // set for no render event.noRender(); } }
Inline Rendering
We have now seen how to set views to be rendered from our handlers. However, we can use three cool methods to render views and layouts on-demand, much how cfinclude is used. These methods exist in the Renderer plugin and several facade methods exist in the super type so you can call it from any plugin, handler, interceptor, view or layout:
renderView()
Render inline views from the parent application or from specific modules according to arguments:
| Argument | Type | Required | Default | Description |
|---|---|---|---|---|
| view | string | false | look in request collection | The name of the view to render |
| cache | boolean | false | false | Cache the view to be rendered |
| cacheTimeout | numeric | false | (provider default) | The timeout in minutes or whatever the cache provider defines |
| cacheLastAccessTimeout | numeric | false | (provider default) | The idle timeout in minutes or whatever the cache provider defines |
| cacheSuffix | string | false | --- | Adds a suffix key to the cached key. Used for providing uniqueness to the cacheable entry |
| module | string | false | --- | The name of the module to render the view from |
| args | struct | false | {} | Local arguments to pass into the view rendering |
| collection | any | false | --- | A collection to use by this Renderer to render the view as many times as the items in the collection (Array or Query) |
| collectionAs | any | false | --- | The name of the collection variable in the partial rendering. If not passed, we will use the name of the view by convention |
| prepostExempt | boolean | false | false | If true, pre/post view interceptors will not be fired. By default they do fire |
renderExternalView()
Render inline views from anywhere in the server according to arguments:
| Argument | Type | Required | Default | Description |
|---|---|---|---|---|
| view | string | true | --- | The name of the view to render |
| cache | boolean | false | false | Cache the view to be rendered |
| cacheTimeout | numeric | false | (provider default) | The timeout in minutes or whatever the cache provider defines |
| cacheLastAccessTimeout | numeric | false | (provider default) | The idle timeout in minutes or whatever the cache provider defines |
| cacheSuffix | string | false | --- | Adds a suffix key to the cached key. Used for providing uniqueness to the cacheable entry |
| module | string | false | --- | The name of the module to render the view from |
| args | struct | false | {} | Local arguments to pass into the view rendering |
renderLayout()
Render nested layouts or view/layout combinations:
| Argument | Type | Required | Default | Description |
|---|---|---|---|---|
| layout | string | true | --- | The name of the explicit layout to render |
| view | string | false | --- | The name of the view to render this layout with |
| module | string | false | --- | The name of the module to render the view from |
| args | struct | false | {} | Local arguments to pass into the view rendering |
Some Examples
<cfoutput> // render inline #renderView('tags/metadata')# // render from a module #renderView(view="security/user",module="security")# // render a view from the handler action function showData(event,rc,prc){ // data here return renderView(view="general/showData"); } // render an email body content in an email template layout body = renderlayout(layout='email',view='templates/email_generic'); </cfoutput>
Let's explore all the things we can do with inline renderings.
Content Variable Views
A content variable is a variable that contains HTML/XML or any kind of visual content that can easily be rendered anywhere. You can easily do this by leveraging inline rendering and placing the content into a variable, usually in an event handler:
function home(event,rc,prc){ // render some content variables with funky arguments prc.sideColumn = renderView(view='tags/sideColumn',cache=true,cacheTimeout=10); // set view event.setView('general/home'); }
So how do I render it?
<div id="content"> <div id="leftColumn"> <cfoutput>#prc.sideColumn#</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.
<div id="content"> <div id="leftColumn"> <cfoutput>#event.getValue(name='sideColumn',default='',private=true)#</cfoutput> </div> <div id="mainView"> <cfoutput>#renderView()#</cfoutput> </div> </div>
So now, if no content variable exists, an empty string will be rendered.
Important: String manipulation in Java relies on immutabile structures, so performance penalities might ensue. If you will be doing a lot of string manipulation, concatenation or rendering, try to leverage native java objects: StringBuilder or StringBuffer
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.
renderExternalView(string view, [boolean cache='false'], [string cacheTimeout=''], [string cacheLastAccessTimeout=''], [string cacheSuffix=''], [any args])
<cfoutput>#renderExternalView(view='/myViewsMapping/tags/footer')#</cfoutput>
Rendering With Local Variables
Passing local variables to layouts and views: renderView(args), renderLayout(args) now get the args argument which can be a structure of data that will be specifically passed to views/layouts for rendering ONLY there. This gives you great DRYness (yes that is a word) when building new and edit forms or views as you can pass distinct arguments to distinguish them and keep structure intact.
Universal Form
<h1>#args.type# User</h1> <form method="post" action="#args.action#"> ... </form>
New Form
#renderView(view='forms/universal',args={type='new',action='user.create'})#
Edit Form
#renderView(view='forms/universal',args={type='edit',action='user.update'})#
Rendering Collections
You have a few arguments in the renderView() method that deal with collection rendering:
- collection : A data collection that can be a query or an array of objects, structs or whatever
- collectionAs : The name of the variable in the variables scope that will hold the collection pivot.
- collectionStartRow : Defaults to 1 or your offset row for the collection rendering
- collectionMaxRows : Defaults to show all rows or you can cap the rendering display
Once you call renderView() with a collection, the renderer will render the view once for each member in the collection. The views have access to the collection via arguments.collection or the member currently iterating. The name of the member being iterated as is by convention the same name as the view. So if we do this in any layout or simple view:
#renderView(view='tags/comment',collection=rc.comments)#
Then the tags/comment will be rendered as many times as the collection rc.comments has members on it and by convention the name of the variable is comment the same as the view name. If you don't like that, then use the collectionAs argument:
#renderView(view='tags/comment',collection=rc.comments,collectionAs='MyComment')#
So let's see the collection view now:
<h1>Title: #comment.getTitle()# (#_counter# of #_items#</h1> <p>Author: #comment.getAuthor()#</p> #comment.getComment()# <hr/>
You can see tha charge to prepare and render the viewlet.
- Create the portable event for the viewlet like any other normal event with one exception.
- The event will return a rendered view or content
Layout Code (Where I place my viewlet call)
<div id="leftbar"> #runEvent(event='viewlets.userinfo',prepostExempt=true)# </div>
This code just renders out the results of a runEvent() method call. I would suggest you look at the API docs to discover all paramters to the runEvent() method call. The prePostExempt argument makes the execution of the event faster as it skips any preEvent/postEvent interception calls.
Event Code (viewlets.userinfo)
function userinfo(event,rc,prc){ // place data in prc and prefix it to avoid collisions prc.userinfo_qData = userService.getUserInfo(); // render out content return renderView("viewlets/userinfo"); }
This handler code is where the magic happens. I talk to a service layer and place some data on the private request collection my viewlet will use. I then return the results of a renderView() call that will render out the exact viewlet I want. You can be more creative and do things like:
- render a layout + view combo
- render data
- return your own custom strings
- etc.
Important : We would suggest you namespace or prefix your private request collection variables for viewlets in order to avoid collisions from multiple viewlet events in the same execution thread.
Viewlet Code (viewlets/userinfo.cfm)
<cfoutput> <div>User Info Panel</div> <div>Username: #prc.userinfo_qData.username#</div> <div>Last Login: #prc.userinfo_qData.lastLogin#</div> </cfoutput>
My view is a normal standard view, it doesn't even know it is a viewlet, remember, views are DUMB!
Funky Viewlets
ColdBox also allows you to have Funky Viewlets and yes that is a real technical term. Funky viewlets allows you to pass arguments to the runEvent() method calls so you can simulate normal method calls and pass extra arguments to the events. So let's make our previous viewlet into a funky viewlet:
function userinfo(event,rc,prc,boolean widget=false){ // place data in prc and prefix it to avoid collisions prc.userinfo_qData = userService.getUserInfo(); // render out content as widget or normal procedures if( arguments.widget ){ return renderView("viewlets/userinfo"); } event.setView('viewelts/userinfo'); }
What have I done? I have added a custom argument to my action signature: bolean widget=false. Then I split the rendering depending on this incoming argument. WOW! So how do I call my funky viewlet?
<div id="leftbar"> #runEvent(event='viewlets.userinfo',prepostExempt=true,eventArguments={widget=true})# </div>
That easy! Just add another argument called eventArguments and now you can pass extra arguments to your event actions and cook up some funky viewlets!
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()>
Am I in an ajax call
<cfset event.isAjax()>
Render an event as a viewlet
<cfoutput>#runEvent(event="nav:main.leftnav",prepostExempt=true)</cfoutput>
What is the current layout?
<cfset event.getCurrentLayout()>
What is the current view
<cfset event.getCurrentView()>
James Brown said
at 02:24:24 PM 11-Oct-2011

SideBar
User Login 





Comments (