The JSF component set available for definition in a Gracelet Library are backed by a set of sub classes that can be
instantiated via the definition provided in the library. Each type will translate
to a true sub class of the base JSF UI component set (Input, Output, Data, Form, etc.,)
and so will work as expected in the JSF lifecycle.
Components that are truly stateful (Form, Input, etc.,) will have the isTransient() on the
UIComponent implementation return false and thus will maintain state between requests.
Example Output Component
To create a component that will create a ul html tag
based list off a value binding we could do the following:
Toggle Line Numbers |
1
namespace = "http://gracelets/doc/example" 2
list = Output { cmp -> cmp.builder.ul { cmp.value.each { val -> li { print val } } } }
|
Here we assign an Output() component to the list variable which will translate into a tag called "list"
available to this component library. Then we accept the first of a few optional parameters, called cmp,
which represents the UIComponent implementation behind this component (which is a true sublcass of
UIOutput in this case). We get a RendererMarkupBuilder for this component by calling the builder attribute
which allows us to render xml markup to the JSF ResponseWriter. We call ul() which will create an html
ul tag. Inside the ul tag we loop through the value binding returned from the component and create
a li html tag and print the toString() value of each item in the list.
Then we could use this tag as shown below:
Toggle Line Numbers |
1
ex = ns."http://gracelets/doc/example" 2
3
ex.list(value: { ["One", "Two", "Three"] })
|
http://somesite/somepage.jsf - Mozilla Firefox
http://somesite/somepage.jsf
http://somesite/somepage.jsf
Example Generic Component
If you do not want to make any particular type of component (Output, Input, so on...) you
can declare a Generic component which is a simple extension to the UIComponentBase class.
These are particularly useful for making non-rendered components. Ones that provide functionality
but do not render anything additional themselves. For instance if we wanted to render children of a particular component depending on
a system wide variable we could do so as follows:
Toggle Line Numbers |
1
showSpecialMenu = Generic { cmp -> if (someSetting) { cmp.renderChildren(); } }
|
When the component is rendered, it will call this default closure as the encodeChildren() method
and since we are using the default closure, the rendersChildren flag is set to true. Here in this closure
we check to see if 'someSetting' in variable context resolution is a groovy true (non-zero, not null and not an empty string)
and if so then we call the automatically available method 'rendersChildren' on the UIComponent that
was passed.
We could then use this tag as follows:
Toggle Line Numbers |
1
ex = ns."http://gracelets/doc/example" 2
3
def someFlag = true 4
someSetting = { someFlag = !someFlag } 5
6
ex.showSpecialMenu { h.outputText("Will Not Be Rendered") } 7
ex.showSpecialMenu { h.outputText("Will Be Rendered") }
|
http://somesite/somepage.jsf - Mozilla Firefox
http://somesite/somepage.jsf
http://somesite/somepage.jsf
Example Command Component
With some components, you still need to understand how the JSF lifecycle works and what other pieces need
to be defined. For instance, a Command component needs a corresponding decoder. Otherwise, althought it gets
rendered, it will never actually do anything when the form is submitted.
For instance, if we wanted to create a Command component that uses the html <button> tag, we could do
the following:
Toggle Line Numbers |
1
import javax.faces.event.ActionEvent 2
3
button = Command(decoder: { cmp, ctx -> 4
String clientId = cmp.getClientId(ctx) 5
if( param[clientId] == clientId ) cmp.queueEvent(new ActionEvent(cmp)) 6
}) { cmp, ctx -> 7
def x = cmp.builder 8
def f = cmp.support.findParent(UIForm.class) 9
10
if (f == null) print "Your action buttons must be inside of a form." 11
12
String clientId = cmp.getClientId(ctx) 13
14
def html = cmp.extractHtmlAttributes() 15
html.name = "button_$clientId" 16
html.onclick = "this.form['$clientId'].value = '$clientId'; this.form.submit();" 17
18
x.input(type: "hidden", name: clientId, value: "") 19
x.button(html) { 20
if (cmp.children.size() == 0) print cmp.attributes.value 21
else cmp.support.renderChildren(); 22
} 23
}
|
When the button is clicked, it uses javascript to set the hidden input value to the same
clientId and then submits the form. When the form is submitted it will post the hidden value
and when the decoder is ran it will then detect that the value of the hidden input is the
correct value and then queue an ActionEvent object that will later be handled by the default
jsf system Action Listener who in turn calls the method expression/binding defined by the
developer.
Thus we can use it as follows:
Toggle Line Numbers |
1
ex = ns."http://gracelets/doc/example" 2
3
h.form { 4
ex.button(action: { }, onmousedown: "alert('This is only a demo and cannot be submitted'); return false;") { print "This is a button component" } 5
}
|
http://somesite/somepage.jsf - Mozilla Firefox
http://somesite/somepage.jsf
http://somesite/somepage.jsf
Example Data Component
Writing some components even though much easier can require a bit of code. For instance making a fully featured
grid renderer is by nature somewhat involved. For now we will make a simple Data component that renders the columns
in a table:
Toggle Line Numbers |
1
import javax.faces.component.UIColumn 2
3
grid = Data { cmp -> 4
def columns = cmp.support.getComponents(UIColumn) 5
6
if (columns.size() == 0) return 7
8
def x = cmp.builder 9
10
x.table(width: cmp.get("width", "100%")) { 11
if (cmp.hasFacet("header")) caption { cmp.renderFacet("header") } 12
13
def headers = false 14
def footers = false 15
16
columns.each { 17
if (it.hasFacet("header")) headers = true 18
if (it.hasFacet("footer")) footers = true 19
} 20
21
tbody { 22
if (headers) tr { columns.each { col -> th { col.renderIfHasFacet("header"); } } } 23
24
cmp.rowIndex = 0 25
while (cmp.rowAvailable) { 26
eventContext.set(cmp.get("var", "row"), cmp.rowData) 27
tr { 28
columns.each { col -> 29
td(col.extractHtmlAttributes()) { col.render() } 30
} 31
} 32
cmp.rowIndex++ 33
} 34
35
if (footers) tr { columns.each { td { it.renderIfHasFacet("footer"); } } } 36
37
if (cmp.hasFacet("footer")) caption { cmp.renderFacet("footer") } 38
} 39
} 40
}
|
Above we use yet another feature for Gracelet Component Developers, a simple call to
getComponents(class) will return all UIComponent children that are a sub class
of the passed class. So first we get the collection of UIColumn children. Then, if
there are actually children, we render an html table, we use some other features
for easily checking and optionally rendering the header and footer facet as well as
the footer and header column facets. Then we simply loop through each row in the
available data model and render each column for each row available.
To use this grid component we could use the following code:
Toggle Line Numbers |
1
ex = ns."http://gracelets/doc/example" 2
3
ex.grid (facets: [header: { print "Example Data Table" }], var: "row", value: DataModel ("exampleDataModel") { ["1","2","3"] }) { 4
h.column(facets: [header: { print "Value" }], align: "center") { print { row } } 5
h.column(facets: [header: { print "Length" }], align: "center") { print { row.length() } } 6
h.column(facets: [header: { print "Hash Code" }], align: "center") { print { row.hashCode() } } 7
}
|
http://somesite/somepage.jsf - Mozilla Firefox
http://somesite/somepage.jsf
http://somesite/somepage.jsf
Example Data TableValue | Length | Hash Code |
---|
1 | 1 | 49 |
2 | 1 | 50 |
3 | 1 | 51 |
Example Form Component
With form components, similiar to command components, you also need to write
a corresponding decoder to flag the submitted boolean to true if the form was
really submitted. Notice the example below:
Toggle Line Numbers |
1
form = Form(decoder: { cmp, ctx -> 2
def clientId = cmp.getClientId(ctx) 3
if ( param[clientId] == clientId ) cmp.submitted = true 4
}) { cmp, ctx -> 5
6
def x = cmp.builder 7
def html = cmp.extractHtmlAttributes() 8
9
def clientId = cmp.getClientId(ctx) 10
11
html.action = actionURL 12
html.method = "post" 13
html.onsubmit = "$html.onsubmit; this['$clientId'].value = '$clientId';" 14
html.id = clientId 15
16
x.form(html) { 17
input(type: "hidden", name: clientId, value: "") 18
print ctx.viewState 19
cmp.renderChildren() 20
} 21
22
}
|
Here we get the xml builder. Then we extract all the valid html attributes so we can pass
them onto the form tag. We make sure we add (or override) the action, method and onsubmit html
attributes. We use the globally available 'actionURL' variable which will contain the full url
to postback to for the currently rendered view. We use a hidden input that we will set only
if this form is submitted. Also we print out the view state provided by this implementation
(which in html is a a special hidden input) required by the JSF system in order to restore view state on postbacks.
This is made easy via the added method to FacesContext called getViewState(). Then we render the children
of this component inside the form tag.
We could use this component as follows:
Toggle Line Numbers |
1
ex = ns."http://gracelets/doc/example" 2
3
ex.form(enctype: "multipart/form-data", onsubmit: "alert('This is only an example'); return false;") { 4
h.commandButton("Click Here") 5
}
|
http://somesite/somepage.jsf - Mozilla Firefox
http://somesite/somepage.jsf
http://somesite/somepage.jsf
Example Renderer
As with regular JSF component development, inside a Gracelet Component Library you can define
abstract renderers that can be applied to more than one component. For instance if we wanted to
write a renderer that will always put a styled div arround certain components, we could do that as follows:
Toggle Line Numbers |
1
divRenderer = Renderer( 2
encodeBegin: { cmp, ctx -> 3
print "<div style=\"border: solid black 1px; padding: 5px; margin-bottom: 10px;\">" 4
}, 5
encodeEnd: { cmp, ctx -> 6
print "</div>"; 7
} 8
) 9
10
header = Output(renderer: divRenderer) { cmp, ctx -> print "Some Header" } 11
12
body = Output(renderer: divRenderer)
|
Toggle Line Numbers |
1
ex = ns."http://gracelets/doc/example" 2
3
ex.header() 4
ex.body { 5
print "Some Body Content" 6
}
|
http://somesite/somepage.jsf - Mozilla Firefox
http://somesite/somepage.jsf
http://somesite/somepage.jsf
Some Header
Some Body Content
Example Render Kit
If you need to write a component library that produces something other than HTML/XHTML, like maybe Voice XML, you can add a render kit
component to your Gracelet Component Library. This can be done as show in the example below:
Toggle Line Numbers |
1
public static final String POST_KEY = "voicexmlViewState" 2
3
namespace = "http://gracelets/voicexml" 4
alias = "vxml" 5
6
VXML_BASIC = RenderKit(requestKey: POST_KEY, getContentType: { "text/xml" }) 7
8
form = Form (decoder: { cmp -> 9
cmp.submitted = param[this.getClientId(cmp)] != null 10
}) { cmp -> 11
def xml = cmp.builder 12
def clientId = this.getClientId(faces, cmp) 13
xml.form (id: clientId) { 14
xml.field(name: POST_KEY, expr: { "'$faces.viewState'" }) 15
xml.field(name: "javax.faces.RenderKitId", expr: "'VXML_BASIC'") 16
17
cmp.renderChildren() 18
} 19
} 20
21
private def getClientId (ctx, cmp) { cmp.getClientId(ctx).replaceAll(":","_") } 22
|
Example Phase Listener
If you need to do some pre-processing or post-processing for your library components
or you simply need to hook into the JSF lifecycle for some other reason, you can do so
easily by defining a phase listener hook in a Gracelet Component Library.
For instance you could easily write a phase hook that allows you to debug when each phase
starts and ends:
Toggle Line Numbers |
1
import javax.faces.event.PhaseId 2
3
debugHook = Phase(PhaseId.ANY_PHASE, { before, phase -> 4
if (before) log.info("Starting phase: ($phase)") 5
else log.info("Ending phase: ($phase)") 6
})
|
Other Components (Graphic, Input, SelectOne, SelectMany, SelectItem, SelectItems, Message, Messages)
The rest of the components will generally not be used often, but are available if you discover
a need to use them. You might define them to change base functionality, like how the component
is decoded, converted, updated, etc., and you want that to be inherent to the component instead of
having to define converters and validators in every use case.
More about components
All of the components that take a renderer
setting can thus reference a string renderer id (defined in faces-config.xml file), a renderer
defined in the library or a dynamic closure. The following example shows how this might be done:
Toggle Line Numbers |
1
someTag = Command(renderer: "javax.faces.Link") 2
3
someRenderer = Renderer { cmp, ctx -> print "Hello World" } 4
5
someOtherTag = Ouptut(renderer: someRenderer) 6
7
someOtherRenderer = Renderer { cmp, ctx -> print "Hello Jupiter" } 8
9
dynaTag = Output(renderer: { cmp -> return cmp.value ? someRenderer : someOtherRenderer }) 10
11
anotherDynaTag = Output(renderer: { cmp -> return cmp.value ? "javax.faces.Link" : "javax.faces.Button"})
|
On the first tag we reference a renderer-id defined in a faces-config.xml, which will
take care of rendering our Command component. After that, we define a renderer right inside
the library and then use that with a Output component. In this way you can
define a permanent renderer for the component.
The last part of the example shows a dynamic way of determining the renderer at component rendering
time, right when the component is to be rendered. In the 'dynaTag' definition we basically say that if
the component has a value we should use 'someRenderer' and if it does not we use 'someOtherRenderer'.
Then in 'anotherDynaTag' we do the same thing, but use renderer-id's instead of library level renderers.