Reference Manual for Gracelets - 2.0.0.RC2SourceForge.net Logo
2.5 - Tutorial : Using Page Components
Some times you would like to be able to have the power of any component inside your page, for a very rare solution or to even prototype or proof of concept a component and do so right inside a particular page.

Also, you may need to have more complex logic very specific to a particular page. In these cases, Gracelets provides you with at least 3 options. 1) Page Level JSF Components, 2) Page Level Renderers, and 3) Page Level Tag Handlers.

Page Level JSF Components

You may want to write a Form, Command, Data or other type of component right inside your page, and thus inside your page having access to exactly what is rendered and having a part on your page where you participate in the decoding phase.

You can create prototypes of the following types: Form, Command, Data, Input and Generic. You specify the type via the 'type' attribute passed to the prototype() tag which can be called on any builder (as long as the builder itself does not have a prototype() tag).

You can specify the 'decoder', 'encodeBegin', 'encodeChildren', 'encodeEnd', 'isRendered' and 'getClientId' as attributes with closures that will be ran as parts of the component decoding and rendering process.

For example, to prototype a security form on a page you could do the following:
Toggle Line Numbers
1 u.prototype(type: "form", decoder: { cmp, ctx -> 
2      
3     def cid = cmp.getClientId(ctx) 
4     if (param["form_$cid"] == "submitted") { 
5         if (param.securityValue != someVerifiedValue) throw new RuntimeException("Security Error") 
6         cmp.submitted = true 
7     } 
8  
9 }, encodeChildren: { cmp, ctx -> 
10  
11     def x = cmp.builder 
12     def cid = cmp.getClientId(ctx) 
13     x.form(action: faces.actionURL, method: "post", onsubmit: "this['form_$cid'].value = 'submitted';") { 
14         input(type: "hidden", name: "form_$cid") 
15         input(type: "hidden", name: "securityValue", value: someComplicatedValue) 
16          
17         fieldset { legend("Fields"); 
18             cmp.renderChildren() 
19         } 
20     } 
21      
22 }) { 
23      
24     print "Password: "; h.inputSecret(value: Value { someBeanValue }) 
25      
26 } 
http://somesite/somepage.jsf - Mozilla Firefox
http://somesite/somepage.jsf
http://somesite/somepage.jsf
Password:

Page Level Renderer

You may simply want to have complete control on how the page is rendered on each page access and don't need or want to write a component to do so, and it could be simple rendering logic very specific to your page. You can accomplish this with page level renderers. You can call the render() tag on any builder. If you pass it a single closure, it will be the encodeChildren() part of a transient component produced for your renderer.

For instance, in the Blog application example, we don't want to have to create a data model and the like just to show the comments on this one page and we don't need a bunch of bindings and closure expressions, but rather we want full control of rendering this part of the page. We can use a page level renderer instead, like so below:
Toggle Line Numbers
1 xh.table(width: "100%") { 
2     u.render { cmp -> 
3      
4         def x = cmp.builder 
5         def blogComments = [[date: new Date(), text: "This is a comment"],[date: new Date(), text: "This is another comment"]] 
6         if (blogComments.size() > 0) { 
7             blogComments.each { comment -> 
8                 x.tr { td (width: 200) {  
9                     print "Comment - " + (comment.date << "MM/dd/yyyy hh:mm:ssa") 
10                 } } 
11                 x.tr { td(colspan: 2) { 
12                     hr(style: "color: blue") 
13                     print comment.text 
14                     br(); br(); br(); 
15                 } } 
16             } 
17         } 
18      
19     } 
20 } 
http://somesite/somepage.jsf - Mozilla Firefox
http://somesite/somepage.jsf
http://somesite/somepage.jsf
Comment - 08/27/2009 08:28:04PM

This is a comment


Comment - 08/27/2009 08:28:04PM

This is another comment


Notice above that the table tag is part of the Facelet tree building process. But everything inside the render() tag is part of the Page rendering process. Thus in this part you have full control of how it is rendered every time right inside your page.

Page Level Tag Handlers

There are many cases where you may want to decide whether to even include a component on a page or not depending on a certain condition. When Facelets was created this was made possible via TagHandler's. However in facelets it still requires alot of setup and scaffolding code to do so. In Gracelets you can use them in Gracelet Component Libraries but also have the option of using them directly in your page/view.

The difference between using TagHandlers and regular components is at what point your code is ran and how it affects the component tree. With regular components you could use the 'rendered' attribute for instance to control whether a component is rendered or not, but that component is still part of the JSF component tree in the view and can still participate in decoding and other parts of the JSF lifecycle.

With TagHandlers you can completely remove a component from the tree depending on a certain condition, or even add a component to the tree many times.

For instance below we render twice the following page each time the tag handler will decide which components are in the tree:
Toggle Line Numbers
1 Factory("counter", "event") { 0 } 
2  
3 u.handler(cond: { 
4     if (counter % 2 != 0) { counterNWas = counter; return true; } else return false 
5 }) { 
6      
7     print { "All children component will be included when $counterNWas is not divisible by two" }  
8      
9 } 
10  
11 u.handler(cond: { 
12     if (counter % 2 == 0) { 
13         counterPWas = counter; counter++; return true;  
14     } else return false   
15 }) { 
16      
17     print { "All children components will be included when $counterPWas is divisible by two" }  
18      
19 } 
http://somesite/somepage.jsf - Mozilla Firefox
http://somesite/somepage.jsf
http://somesite/somepage.jsf
All children components will be included when 0 is divisible by two
http://somesite/somepage.jsf - Mozilla Firefox
http://somesite/somepage.jsf
http://somesite/somepage.jsf
All children component will be included when 1 is not divisible by two
The counter variable being used is manipulated by the tag handlers at TREE CREATION TIME while the sub components that are under them will render their values at RENDER TIME, so we have to store the tree creation time value into another variable so as to be able to access its value at render time.