One main feature that allows gracelet scripts to be able to harness the large
component set already existing in JSF and Facelets are Gracelet Builders. Any library
already defined in a Facelets .taglib.xml file is accessible to a gracelet script.
To see a list of automatically available libraries refer to the
Introduction : How it Works : View Scripts section.
In an attempt to avoid confusion, it is good to clarify the difference between a tag library and a builder,
although sometimes we use the term interchangeably. A tag library is a Facelet tag library defined in XML in the
.taglib.xml format or programatically defined like the core libraries are in Facelets. A gracelet builder
represents a tag library, and is the proxy in gracelets that allows you to access all the power in the tag
libraries. Since they are so closely related and these details are really transparent to the developer
we will use both terms to refer to builders themselves.
For general xml markup, you use the "xh" bound builder. For example if you want to
build a simple hello world page, but with proper xhtml, you could do the following:
Using xh, the XHTML builderToggle Line Numbers |
1
xh.html { 2
3
head { title("Hello World Example") } 4
5
body { 6
print { "Hello World @ " + new Date() } 7
} 8
9
}
|
Hello World Example - Mozilla Firefox
http://somesite/somepage.jsf
Hello World Example
Hello World ExampleHello World @ Thu Aug 27 20:28:01 CDT 2009
One very important point with the markup builder, and really this is an easy "gotcha" in XHTML source files, is
that markup is just markup. Thus many times when you expect JSF to consider markup tags as a child components you
don't get what you would expect. The JSF Core
facet tag is a good example. Notice the following two examples:
Incorrect Facet DefinitionToggle Line Numbers |
1
h.dataTable(value: { someDataModel }) { 2
3
j.facet(name: "header") { 4
xh.a(href: "http://somesite/somepage") { 5
h.graphicImage(url: "someimg.png", style: "border: 0") 6
} 7
} 8
9
}
|
Correct Facet DefinitionToggle Line Numbers |
1
h.dataTable(value: { someDataModel }) { 2
3
j.facet(name: "header") { 4
u.component { 5
xh.a(href: "http://somesite/somepage") { 6
h.graphicImage(url: "someimg.png", style: "border: 0") 7
} 8
} 9
} 10
11
}
|
Since a JSF Core
facet component requires that it be passed only 1 child component the first example
is incorrect. Since markup tags are only markup and not container JSF components, the
a and
img tags called on the "xh" builder really get translated into 2 text components. 1 component
for the text before the graphicImage component and 1 component for the text after it. In other words the
markup is on the same level in the component tree as the graphic image even though in definition
it appears to be a child. Thus in the second example we encapsulate them all in a true JSF component,
in this case, the
component tag of the Facelet UI tag library.
Tag Attributes
Builder tag attributes are similiar to the print() and println() statements in that
if you pass a static value the attribute will always be the same, where as if you pass
inline EL expressions or closure expressions the attribute values will now be dynamic.
The following two examples will demonstrate this:
Setting static attributeToggle Line Numbers |
1
import java.text.SimpleDateFormat 2
3
def f = new SimpleDateFormat("D") 4
5
xh.html { 6
7
head { 8
title("Hello World Example") 9
style { 10
print """ 11
.body_class1 { font-family: Tahoma; } 12
.body_class2 { font-family: "Times New Roman"; } 13
""" 14
} 15
} 16
17
body { 18
div(class: Integer.parseInt(f.format(new Date())) % 2 == 0 ? "body_class1" : "body_class2") { 19
print { "Hello World @ " + new Date() } 20
} 21
} 22
23
}
|
Hello World Example - Mozilla Firefox
http://somesite/somepage.jsf
Hello World Example
Hello World ExampleHello World @ Thu Aug 27 20:28:01 CDT 2009
Setting attribute with closure expressionToggle Line Numbers |
1
import java.text.SimpleDateFormat 2
3
def f = new SimpleDateFormat("D") 4
5
xh.html { 6
7
head { 8
title("Hello World Example") 9
style { 10
print """ 11
.body_class1 { font-family: Tahoma; } 12
.body_class2 { font-family: "Times New Roman"; } 13
""" 14
} 15
} 16
17
body { 18
div(class: { Integer.parseInt(f.format(new Date())) % 2 != 0 ? "body_class1" : "body_class2" }) { 19
print { "Hello World @ " + new Date() } 20
} 21
} 22
23
}
|
Hello World Example - Mozilla Firefox
http://somesite/somepage.jsf
Hello World Example
Hello World ExampleHello World @ Thu Aug 27 20:28:01 CDT 2009
The only difference between the last two examples is that in the second one the
class attribute
is wrapped with curly braces (
{ ... }), which turns it into a closure expression, and therefore
is dynamic.
In the first example the attribute value is somewhat dynamic, but once the page is compiled it will always be the
same, so it depends on when the first person loaded the page. While in the second one, using a closure
expression makes this truly dynamic, it will constantly recalculate the attribute on each time the attribute value
is requested.
Common Builders
The Facelet UI library (u), the JSF Core library (j) and the JSF Html library (h) are among the
automatically imported and available libraries in gracelet scripts. The following example
will use common builders to create a registration page:
Registration PageToggle Line Numbers |
1
xh.html(docType: "transitional") { 2
head { 3
title("Example template using common builders") 4
style("body { font-family: Tahoma; }") 5
} 6
7
body { 8
9
h.form { 10
xh.fieldset { 11
legend("Registration Form") 12
table (width: "100%") { 13
tr { td("First"); td { 14
h.inputText(value: Value({ regBean.first }), required: true) { 15
j.validateLength(maximum: 20) 16
} 17
} } 18
tr { td("Last"); td { 19
h.inputText(value: Value({ regBean.last })) 20
} } 21
tr { td("Address"); td { 22
h.inputText(value: Value({ regBean.address })) 23
} } 24
tr { td("City"); td { 25
h.inputText(value: Value({ regBean.city })) 26
} } 27
tr { td("State"); td { 28
h.inputText(value: Value({ regBean.state })) 29
} } 30
tr { td("Country"); td { 31
h.inputText(value: Value({ regBean.country })) 32
} } 33
tr { 34
td(colspan: 2, align: "right") { 35
h.commandButton("Cancel", immediate: true, action: "home") 36
h.commandButton("Signup", action: { 37
someActionBean.signup() 38
facesMessages.add("You have been signed up!") 39
return "home" 40
}) 41
} 42
} 43
} 44
} 45
} 46
47
} 48
}
|
All the code in
red is using the markup builder, this will simply output
regular markup, in this case XHTML. One small detail is the
docType attribute on the first tag called.
This is an easier way of having the
<!DOCTYPE ... specified on a page.
All the code in
green is using the JSF Html builder, thus the
form tag is
really going to create a JSF HTML form component in the JSF tree. Next we see code in this color calling the
inputText
tag to create each input field on the form. We also see the
commandButton tag used twice to create a
"Cancel" and "Signup" buttons for the form.
On line 15 we see code in
blue. This is using the JSF Core tag library to put a validator
on the first input component making sure the length of the input as no more than 20 characters when submitted.
Finally the code on lines
37 to 39 is action code that will be executed when the Signup button is pushed and the form
is submitted to the server. It calls the signup() method on the
someActionBean bean available to the variable contexts.
Then we add a faces message to tell the user that he has been signed up and finally return the "home" outcome which
will direct the user to the home page.
One thing that may stand out is that you need to qualify what builder you want to use in order to have the right JSF
component added to the view. However, if you are using the same builder for children tags you don't have to qualify the
builder. Thus on line 1 we qualify the builder with "xh.", but on line 2 we don't need to put "xh." before the
head tag
since its parent is using the same builder.
Gracelet tag library/builder
Gracelet comes packaged with one tag library that is automatically available and imported into any script with the bound
variable
g. This library is small right now, but expect it to grow as needed and useful components that are really missing
are discovered and designed. One component that is available already though is a sub page component. This allows one to define sub
views inside of the gracelet page easily.
This can work well for list/edit views, where you want a list of items and then have a form to be able to edit them. With the
subpages component, you can do both in a single view, with page-level navigation. The main component's value binding is used
to maintain state about which sub page is to be decoded/rendered. You can set the value binding to a particular value, like in an action closure,
in order to change the sub page. The following example will illustrate this:
Sub Page NavigationToggle Line Numbers |
1
xh.html { 2
3
g.subpages( value: Value({ userManagerNav }) ) { 4
5
u.component(page: "list") { 6
7
h.dataTable(value: { userList }, var: "user") { 8
j.facet(name: "header") { print "User List" } 9
10
h.column { 11
j.facet(name: "header") { print "Username" } 12
print { user.username } 13
} 14
15
h.column { 16
j.facet(name: "header") { print "Real Name" } 17
print { user.name } 18
} 19
20
h.column { 21
j.facet(name: "header") { print "Options" } 22
h.commandLink(action: { 23
selectedUser = user 24
userManagerNav = "edit" 25
}) { 26
xh.img(src: "edit.png", border: 0, title: { "Edit User - $user.name" }) 27
} 28
} 29
} 30
31
} 32
33
u.component(page: "edit") { 34
35
h.form { 36
print "Username: "; h.inputText(value: Value({ selectedUser.username })); xh.br() 37
print "Real Name: "; h.inputText(value: Value({ selectedUser.name })); xh.br() 38
print "Password: "; h.inputSecret(value: Value({ selectedUser.password })); xh.br() 39
xh.hr() 40
h.commandButton("Cancel", immediate: true, action: { userManagerNav = "list" }) 41
h.commandButton("Save Changes", action: { 42
someActionBean.saveUser(selectedUser) 43
facesMessages.add("User changes saved") 44
userManagerNav = "list" 45
}) 46
} 47
48
} 49
50
} 51
52
}
|
Conclusions about builders
On alot of gracelet pages you will be able to dive right into composing your page, not worrying about importing
the common libraries that are usually imported to just about every script. You simply call the tag name on the library
you want and that tag will be called to compose its part of the JSF view. Also, attributes can be static, semi-static or completely
dynamic using closure expressions.