/
faces-components.xml
461 lines (388 loc) · 18 KB
/
faces-components.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" []>
<chapter id="components">
<title>Seam Faces Components</title>
<para>
While Seam Faces does not provide layout components or other UI-design related features, it does provide
functional components designed to make developing JSF applications easier, more functional, more
scalable, and more practical.
</para>
<para>
For layout and design components, take a look at <ulink
url="http://jboss.org/richfaces">RichFaces</ulink>, a UI component library specifically tailored for
easy, rich web-interfaces.
</para>
<section id="basicUsage">
<title>Introduction</title>
<para>
In order to use the Seam Faces components, you must first add the namespace to your view file,
just like the standard JSF component libraries.
</para>
<programlisting role="XML"><![CDATA[<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:s="http://jboss.org/seam/faces"
xmlns:ui="http://java.sun.com/jsf/facelets">
<h1>Welcome to Seam Faces!</h1>
<p>
This view imports the Seam Faces component library.
Read on to discover what components it provides.
</p>
</html>]]></programlisting>
<tip>
<para>
All Seam Faces components use the following namespace:
<literal>http://jboss.org/seam/faces</literal>
</para>
</tip>
</section>
<section id="validateForm">
<title><s:validateForm></title>
<para>
On many occasions you might find yourself needing to compare the values of multiple input fields
on a given page submit: confirming a password; re-enter password; address lookups; and so on.
Performing cross-field form validation is simple - just place the <s:validateForm> component
in the form you wish to validate, then attach your custom Validator.
</para>
<programlisting role="XML"><![CDATA[<h:form id="locationForm">
<h:inputText id="city" value="#{bean.city}" />
<h:inputText id="state" value="#{bean.state}" />
<h:inputText id="zip" value="#{bean.zip}" />
<h:commandButton id="submit" value="Submit" action="#{bean.submitPost}" />
<s:validateForm validatorId="locationValidator" />
</h:form>]]></programlisting>
<para>
The corresponding Validator for the example above would look something like this:
</para>
<programlisting role="JAVA"><![CDATA[@FacesValidator("locationValidator")
public class LocationValidator implements Validator
{
@Inject
Directory directory;
@Inject
@InputField
private Object city;
@Inject
@InputField
private Object state;
@Inject
@InputField
private ZipCode zip;
@Override
public void validate(final FacesContext context, final UIComponent comp, final Object values)
throws ValidatorException
{
if(!directory.exists(city, state, zip))
{
throw new ValidatorException(
new FacesMessage("Sorry, that location is not in our database. Please try again."));
}
}
}]]></programlisting>
<tip>
<para>
You may inject the correct type directly.
<programlisting><![CDATA[@Inject
@InputField
private ZipCode zip;]]></programlisting>
</para>
</tip>
<para>
Notice that the IDs of the inputText components match the IDs of your Validator
@InputFields; each @Inject @InputField member will be injected with the value of the form input field
who's ID matches the name of the variable.
</para>
<para>
In other words - the name of the @InputField annotated member variable will automatically
be matched to the ID of the input component, unless overridden by using a field
ID alias (see below.)
</para>
<programlisting role="XML"><![CDATA[<h:form id="locationForm">
<h:inputText id="cityId" value="#{bean.city}" />
<h:inputText id="stateId" value="#{bean.state}" />
<h:inputText id="zip" value="#{bean.zip}" />
<h:commandButton id="submit" value="Submit" action="#{bean.submitPost}" />
<s:validateForm fields="city=cityId state=stateId" validatorId="locationValidator" />
</h:form>]]></programlisting>
<para>
Notice that "zip" will still be referenced normally; you need only
specify aliases for fields that differ in name from the Validator @InputFields.
</para>
<tip>
<para>
<literal>Using @InputField("customID")</literal> with an ID override can also be used to specify a
custom ID, instead of using the default: the name of the field. This gives you the ability to
change the name of the private field, without worrying about changing the name of input fields in
the View itself.
<programlisting><![CDATA[@Inject
@InputField("state")
private String sectorTwo;]]></programlisting>
</para>
</tip>
<para>
An alternate way of accessing those fields on the validator by injecting an InputElement. It works similarly
to @InputField, but stores the clientId and a JSF UIComponent, along with the field value.
</para>
<programlisting role="JAVA"><![CDATA[@FacesValidator("fooValidator")
public class FooValidator implements Validator {
@Inject
private InputElement<String> firstNameElement;
@Inject
private InputElement<String> lastNameElement;
@Inject
private InputElement<Date> startDateElement;
@Inject
private InputElement<Date> endDateElement;
...
}]]></programlisting>
<para>
Use get methods to access those information
</para>
<programlisting role="JAVA"><![CDATA[public void validate(final FacesContext ctx, final UIComponent form, final Object value) throws ValidatorException {
Date startDate = startDateElement.getValue();
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DAY_OF_MONTH, -1);
if (startDate.before(calendar.getTime())) {
String message = messageBuilder.get().key(new DefaultBundleKey("booking_checkInNotFutureDate"))
.targets( startDateElement.getClientId() ).build().getText();
throw new ValidatorException(new FacesMessage(message));
}
...
}]]></programlisting>
</section>
<section id="viewaction">
<title><s:viewAction></title>
<para>
The view action component (<literal>UIViewAction</literal>) is an <literal>ActionSource2</literal>
<literal>UIComponent</literal> that specifies an application-specific command (or action), using
using an EL method expression, to be invoked during one of the JSF lifecycle phases proceeding Render
Response (i.e., view rendering).
</para>
<para>
View actions provide a lightweight front-controller for JSF, allowing the application to accommodate
scenarios such as registration confirmation links, security and sanity checking a request (e.g.,
ensuring the resource can be loaded). They also allow JSF to work alongside action-oriented
frameworks, and existing applications that use them.
</para>
<section>
<title>Motivation</title>
<para>
JSF employs an event-oriented architecture. Listeners are invoked in response to user-interface
events, such as the user clicking on a button or changing the value of a form input. Unfortunately,
the most important event on the web, a URL request (initiated by the user clicking on a link,
entering a URL into the browser's location bar or selecting a bookmark), has long been overlooked in
JSF. Historically, listeners have exclusively been activated on postback, which has led to the common
complaint that in JSF, "everything is a POST."
</para>
<para>
<emphasis>We want to change that perception.</emphasis>
</para>
<para>
Processing a URL request event is commonly referred to as bookmarkable or GET support. Some GET
support was added to JSF 2.0 with the introduction of view parameters and the pre-render view event.
View parameters are used to bind query string parameters to model properties. The pre-render view
event gives the developer a window to invoke a listener immediately prior to the view being rendered.
</para>
<para>
<emphasis>That's a start.</emphasis>
</para>
<para>
Seam brings the GET support full circle by introducing the view action component. A view action is
the compliment of a <literal>UICommand</literal> for an initial (non-faces) request. Like its
cohort, it gets executed by default during the Invoke Application phase (now used on both faces
and non-faces requests). A view action can optionally be invoked on postback as well.
</para>
<para>
View actions (<literal>UIViewAction</literal>) are closely tied to view parameters
(<literal>UIViewParameter</literal>). Most of the time, the view parameter is used to populate the
model with data that is consumed by the method being invoked by a <literal>UIViewAction</literal>
component, much like form inputs populate the model with data to support the method being invoked
by a <literal>UICommand</literal> component.
</para>
</section>
<section>
<title>Usage</title>
<para>
Let's consider a typical scenario in web applications. You want to display the contents of a blog
entry that matches the identifier specified in the URL. We'll assume the URL is:
</para>
<programlisting>http://localhost:8080/blog/entry.jsf?id=10</programlisting>
<para>
We'll use a view parameter to capture the identifier of the entry from the query string and a view
action to fetch the entry from the database.
</para>
<programlisting role="XML"><![CDATA[<f:metadata>
<f:viewParam name="id" value="#{blogManager.entryId}"/>
<s:viewAction action="#{blogManager.loadEntry}"/>
</f:metadata>]]></programlisting>
<tip>
<para>
The view action component must be declared as a child of the view metadata facet (i.e.,
<literal><f:metadata></literal>) so that it gets incorporated into the JSF lifecycle on both
non-faces (initial) requests and faces (postback) requests. If you put it anywhere else in the
page, the behavior is undefined.
</para>
</tip>
<warning>
<para>
The JSF 2 specification specifies that there must be at least one view parameter for the view metadata
facet to be processed on an initial request. This requirement was introduced into the JSF specification
inadvertently. But not to worry. Seam Faces inserts a placeholder view parameter into the view metadata
if contains other components but no view parameters. That means you can use a view action without a view
parameter, contrary to the JSF specification.
</para>
</warning>
<para>
What do we do if the blog entry can't be found? View actions support declarative navigation just like
<literal>UICommand</literal> components. So you can write a navigation rule that will be consulted before
the page is rendered. If the rule matches, navigation occurs just as though this were a postback.
</para>
<programlisting role="XML"><![CDATA[<navigation-rule>
<from-view-id>/entry.xhtml</from-view-id>
<navigation-case>
<from-action>#{blogManager.loadEntry}</from-action>
<if>#{empty entry}</if>
<to-view-id>/home.xhtml</to-view-id>
<redirect/>
</navigation-case>
</navigation-rule>]]></programlisting>
<para>
After each view action is invoked, the navigation handler looks for a navigation case that matches
the action's EL method signature and outcome. If a navigation case is matched, or the response is
marked complete by the action, subsequent view actions are short-circuited. The lifecycle then
advances appropriately.
</para>
<para>
By default, a view action is not executed on postback, since the primary intention of a view
action is to support a non-faces request. If your application (or use case) is decidedly stateless,
you may need the view action to execute on any type of request. You can enable the view action
on postback using the <literal>onPostback</literal> attribute:
</para>
<programlisting role="XML"><![CDATA[<s:viewAction action="#{blogManager.loadEntry}" onPostback="true"/>]]></programlisting>
<para>
You may only want the view action to be invoked under certain conditions. For instance, you may only
need it to be invoked if the conversation is transient. For that, you can use the
<literal>if</literal> attribute, which accepts an EL value expression:
</para>
<programlisting role="XML"><![CDATA[<s:viewAction action="#{blogEditor.loadEntry}" if="#{conversation.transient}"/>]]></programlisting>
<para>
There are two ways to control the phase in which the view action is invoked. You can set the
<literal>immediate</literal> attribute to true, which moves the invocation to the Apply Request Values phase
instead of the default, the Invoke Application phase.
</para>
<programlisting role="XML"><![CDATA[<s:viewAction action="#{sessionManager.validateSession}" immediate="true"/>]]></programlisting>
<para>
You can also just specify the phase directly, using the name of the phase constant in the
<literal>PhaseId</literal> class (the case does not matter).
</para>
<programlisting role="XML"><![CDATA[<s:viewAction action="#{sessionManager.validateSession}" phase="APPLY_REQUEST_VALUES"/>]]></programlisting>
<tip>
<para>
The valid phases for a view action are:
</para>
<itemizedlist>
<listitem>
<para><literal>APPLY_REQUEST_VALUES</literal> (default if <literal>immediate="true"</literal>)</para>
</listitem>
<listitem>
<para><literal>UPDATE_MODEL_VALUES</literal></para>
</listitem>
<listitem>
<para><literal>PROCESS_VALIDATIONS</literal></para>
</listitem>
<listitem>
<para><literal>INVOKE_APPLICATION</literal> (default)</para>
</listitem>
</itemizedlist>
</tip>
<para>
If the phase is set, it takes precedence over the immediate flag.
</para>
</section>
<section>
<title>View actions vs the PreRenderViewEvent</title>
<para>
The purpose of the view action is similar to use of the PreRenderViewEvent. In fact, the code to
load a blog entry before the page is rendered could be written as:
</para>
<programlisting role="XML"><![CDATA[<f:metadata>
<f:viewParam name="id" value="#{blogManager.entryId}"/>
<f:event type="preRenderView" listener="#{blogManager.loadEntry}"/>
</f:metadata>]]></programlisting>
<para>
However, the view action has several important advantages:
</para>
<itemizedlist>
<listitem>
<para>It's lightweight</para>
</listitem>
<listitem>
<para>It's timing can be controlled</para>
</listitem>
<listitem>
<para>It's contextual</para>
</listitem>
<listitem>
<para>It can trigger navigation</para>
</listitem>
</itemizedlist>
<para>
View actions are lightweight because they get processed on a non-faces (initial) request
<emphasis>before</emphasis> the full component tree is built. When the view actions are invoked,
the component tree only contains view metadata.
</para>
<para>
As demonstrated above, you can specify a prerequisite condition for invoking the view action,
control whether it's invoked on postback, specify the phase in which it's invoked and tie the
invocation into the declarative navigation system. The PreRenderViewEvent is quite basic in
comparison.
</para>
</section>
</section>
<section id="UIInputContainer">
<title>UI Input Container</title>
<para>
UIInputContainer is a supplemental component for a JSF 2.0 composite component encapsulating
one or more input components (EditableValueHolder), their corresponding message components
(UIMessage) and a label (HtmlOutputLabel).
</para>
<para>
This component takes care of wiring the label to
the first input and the messages to each input in sequence. It also assigns two implicit attribute
values, "required" and "invalid" to indicate that a required input field is present and whether
there are any validation errors, respectively. To determine if a input field is required, both
the required attribute is consulted and whether the property has Bean Validation constraints.
</para>
<para>
Finally, if the "label" attribute is not provided on the composite component, the label value will
be derived from the id of the composite component, for convenience.
</para>
<para>
Composite component definition example (minus layout):
</para>
<programlisting role="XML"><![CDATA[<cc:interface componentType="org.jboss.seam.faces.InputContainer"/>
<cc:implementation>
<h:outputLabel id="label" value="#{cc.attrs.label}:" styleClass="#{cc.attrs.invalid ? 'invalid' : ''}">
<h:outputText styleClass="required" rendered="#{cc.attrs.required}" value="*"/>
</h:outputLabel>
<cc:insertChildren/>
<h:message id="message" errorClass="invalid message" rendered="#{cc.attrs.invalid}"/>
</cc:implementation>]]></programlisting>
<para>
Composite component usage example:
</para>
<programlisting role="XML"><![CDATA[<example:inputContainer id="name">
<h:inputText id="input" value="#{person.name}"/>
</example:inputContainer>]]></programlisting>
<tip>
<para>
NOTE: Firefox does not properly associate a label with the target input if the input id
contains a colon (:), the default separator character in JSF. JSF 2 allows developers to
set the value via an initialization parameter (context-param in web.xml) keyed to
<literal>javax.faces.SEPARATOR_CHAR</literal>. We recommend that you override this setting to make the
separator an underscore (_).
</para>
</tip>
</section>
</chapter>