RCFaces 1.2.1
User Guide
Rcfaces or Rich Client Faces is a JavaServerFaces library that provides a set of components for building modern Web applications. Rcfaces uses AJAX technologies and an object-oriented JavaScript API to simplify the building of highly interactive user interfaces.
This open source (GNU LGPL License) library has been developed by Vedana since the summer 2004.
Rcfaces is currently used by a French public organisation for a major project :
The distinctive advantages of Rcfaces are :
Ability to create desktop-like applications that run inside the browser.
Simplify the use of potentially complex technologies like AJAX and JavaScript.
"On the fly" client-side field validation.
The project is hosted on SourceForge : http://sourceforge.net/projects/rcfaces/
Vedana can offer professionnal services and support for projects using Rcfaces (contact us).
Rcfaces works with Java 1.4 or higher. The current version is compiled with Java 1.4.
Rcfaces has been tested on the following J2EE servers :
Rcfaces was developped with Apache Tomcat.
For integration build:
The rcfaces-imageIO-*.jar is used by Rcfaces to generate on the fly multiple versions of an image (hover, disabled, scaled, ...).
<servlet>
<servlet-name>Rcfaces resources Servlet</servlet-name>
<servlet-class>org.rcfaces.renderkit.html.internal.resource.ResourcesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Rcfaces resources Servlet</servlet-name>
<url-pattern>/rc-fwk/*</url-pattern>
</servlet-mapping>
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<display-name>RcfacesFormSample</display-name>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<context-param>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>server</param-value>
</context-param>
<context-param>
<param-name>javax.servlet.jsp.jstl.fmt.localizationContext</param-name>
<param-value>resources.application</param-value>
</context-param>
<listener>
<listener-class>com.sun.faces.config.ConfigureListener</listener-class>
</listener>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>Rcfaces resources Servlet</servlet-name>
<servlet-class>org.rcfaces.renderkit.html.internal.resource.ResourcesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.jsf</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Rcfaces resources Servlet</servlet-name>
<url-pattern>/rc-fwk/*</url-pattern>
</servlet-mapping>
</web-app>
We want to add data from a form into a data grid, so we need to build a JSF managed bean to get fields value and put them in the table's data model. The best-practice solution would be to create two managed beans; however, for this simple demo, one will suffice.
Create a java object named GridBean in the org.rcfaces.rcfacesformsample.managed package :
package org.rcfaces.rcfacesformsample.managed;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.rcfaces.core.event.SelectionEvent;
public class GridBean {
private static final DateFormat dateFormat = new SimpleDateFormat(
"dd/MM/yyyy");
private List<Row> rows; // Data model of dataGrid
private Row newRow; // Use by the form field
public GridBean() {
rows = new ArrayList<Row>();
rows.add(new Row("Didier", "La Rochelle", "1/1/1882"));
rows.add(new Row("Fred", "La Rochelle", "02/02/1901"));
rows.add(new Row("Olivier", "La Rochelle", "11/4/1975"));
rows.add(new Row("Christine", "La Rochelle", "4/12/1980"));
rows.add(new Row("Jean-Marc", "La Rochelle", "4/12/1980"));
rows.add(new Row("JB", "La Rochelle", "1/8/2000"));
}
// called by the actionListner of submit button
public void addRow(SelectionEvent event) {
rows.add(new Row(newRow.getName(), newRow.getCity(), newRow
.getBirthDate()));
newRow = null;
}
public List<Row> getRows() {
return rows;
}
public void setRows(List<Row> rows) {
this.rows = rows;
}
public Row getNewRow() {
if (newRow == null) {
newRow = new Row();
}
return newRow;
}
public void setNewRow(Row newRow) {
this.newRow = newRow;
}
public class Row {
private String name;
private String city;
private Date birthDate;
public Row() {
}
public Row(String name, String city, String birthDate) {
this.name = name;
this.city = city;
try {
this.birthDate = dateFormat.parse(birthDate);
} catch (ParseException ex) {
throw new IllegalArgumentException(ex);
}
}
public Row(String name, String city, Date birthDate) {
this.name = name;
this.city = city;
this.birthDate = birthDate;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public Date getBirthDate() {
return birthDate;
}
public void setBirthDate(Date birthDate) {
this.birthDate = birthDate;
}
}
}
<managed-bean>
<managed-bean-name>gridBean</managed-bean-name>
<managed-bean-class>org.rcfaces.rcfacesformsample.managed.GridBean</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
<?xml version="1.0" encoding="UTF-8"?>
<jsp:root version="2.0" xmlns="http://www.w3.org/1999/xhtml"
xmlns:jsp="http://java.sun.com/JSP/Page">
<jsp:forward page="form.jsf"/>
</jsp:root>
<?xml version="1.0" encoding="UTF-8"?>
<jsp:root version="2.0" xmlns="http://www.w3.org/1999/xhtml"
xmlns:jsp="http://java.sun.com/JSP/Page"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:v="http://rcfaces.org/core" xmlns:vh="http://rcfaces.org/html">
<jsp:directive.page contentType="text/html" pageEncoding="UTF-8" />
<jsp:output omit-xml-declaration="true" doctype-root-element="html"
doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN"
doctype-system="http://www.w3.org/TR/html4/loose.dtd" />
<f:view>
<html>
<head>
<vh:init /> <!-- !important tag -->
</head>
<vh:javaScriptCollector>
<body>
<h:form>
<v:fieldSet text="Form">
<!-- container -->
<div style="margin-top: 10px;">
What is your name :
<v:message for="field1" showDetail="false" showSummary="true"
showIfMessage="true" />
<v:textEntry id="field1" columnNumber="30"
value="#{gridBean.newRow.name}" required="true" />
</div>
<div style="margin-top: 10px;">
Where do you come from :
<v:message for="field2" showDetail="false" showSummary="true"
showIfMessage="true" />
<v:textEntry id="field2" columnNumber="30"
value="#{gridBean.newRow.city}" required="true"
emptyMessage="your city" />
</div>
<div style="margin: 10px 0;">
When were you born :
<v:message for="fieldDate" showDetail="false" showSummary="true"
showIfMessage="true" />
<v:dateEntry id="fieldDate" value="#{gridBean.newRow.birthDate}"
required="true" />
<v:dateChooser id="date" for="fieldDate" />
</div>
<v:submitButton text="submit" actionListener="#{gridBean.addRow}" />
</v:fieldSet>
<div style="margin-top: 10px;">
<v:dataGrid id="tableId"
width="450" value="#{gridBean.rows}" var="row" rows="3">
<v:dataColumn text="Name" sortListener="alpha" value="#{row.name}" />
<v:dataColumn text="city" sortListener="alpha" value="#{row.city}" />
<v:dataColumn text="BirthDate" value="#{row.birthDate}"
alignment="center" />
</v:dataGrid>
<v:pager for="tableId"></v:pager>
</div>
</h:form>
</body>
</vh:javaScriptCollector>
</html>
</f:view>
</jsp:root>
You should see :
For a complete description and demo of each component go to rcfaces.org. You will find javadoc, javascript-doc and TLD-doc.
Each component have some capabilities and inherit some javaScript classes.
http://www.rcfaces.org/comps/comp-basic-info.html
http://www.rcfaces.org/comps/comp-nonvisual-info.html
Accelerator: allows to associate an accelerator key to an action or another component.
FocusManager: allows to deal with the focus on the current page.
Service: allows to (synchronously or asynchronously) call AJAX services from the client.
http://www.rcfaces.org/comps/comp-entry-info.html
http://www.rcfaces.org/comps/comp-complex-info.html
In the coming chapter, you will find:
a quick description of each Rcfaces servlet,
the list and explanation of their configuration parameters,
and finally all the tips to optimize the javascript loading.
If you choose to use Rcfaces for a real-world project, you will have to understand thoroughly this part of the framework.
Rcfaces works with a servlet named "Rcfaces resources Servlet". Its role is to provide stylesheet, javascript and image files used by the Rcfaces components.
Register this servlet like this in web.xml :
<servlet>
<servlet-name>Rcfaces resources Servlet</servlet-name>
<servlet-class>org.rcfaces.renderkit.html.internal.resource.ResourcesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Rcfaces resources Servlet</servlet-name>
<url-pattern>/rc-fwk/*</url-pattern>
</servlet-mapping>
This servlet can be parametrized to define sets of RcFaces components.
The RCFaces servlet is customized by using <init-param> tags.
i.e :
<servlet>
<servlet-name>Rcfaces resources Servlet</servlet-name>
<servlet-class>org.rcfaces.renderkit.html.internal.resource.ResourcesServlet</servlet-class>
<init-param>
<param-name>
org.rcfaces.renderkit.html.javascript.modules.GROUP_ALL_FILES
</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>org.rcfaces.renderkit.html.javascript.sets.CORE</param-name>
<param-value>
basicComponent, tree, toolbar, dialog, image, extraComponent, expandBar, itemsList, gridHelp
</param-value>
</init-param>
<init-param>
<param-name>org.rcfaces.renderkit.html.javascript.sets.EXTRAS</param-name>
<param-value>
validator, messageComponents, popupComponent, tabbedPane, calendar, asyncRender,
suggest, pager, dataGrid, items
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Rcfaces resources Servlet</servlet-name>
<url-pattern>/rc-fwk/*</url-pattern>
</servlet-mapping>
Setting the parameter "GROUP_ALL_FILE" to "false" will result in having all the files loaded but not gathered in a unique file (default value is true).
The goal (GROUP_ALL_FILE=true) is to improve loading of resources by concatenating the javasript and CSS ressources into single files. The load time of this unique resource will be shorter than the combined loading time of the individual component ressources.
The size of HTTP exchanges tends to increase with modern Web applications.
To minimize the network traffic, Rcfaces can add expiration dates to the static resources (JavaScript, css, images, ...) used by JSF components.
With expiration dates, the browser or the proxy server will put the Rcfaces resources in a cache and will not make additional requests for those resources.
The main problem with this mechanism resides in the deployment of a new version of an application. If the cached resources have been modified, there is normally no way to tell the proxy or the browsers to clear their cache immediatly !
The role of the Application Version Servlet (AVS) is to get around this problem. To do so the AVS manages HTTP header for each Rcfaces resource (Last Modified, Expires date, a special calculation of Etag and the content-MD5 attributes). It will also change the URL of the resources whenever a new version of an application is deployed. The change of URL will force the browser and the proxy server to get a new version of all the resources used by an application.
Register this servlet like this :
<servlet>
<servlet-name>Rcfaces Application version</servlet-name>
<servlet-class> org.rcfaces.core.internal.version.ApplicationVersionServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Rcfaces Application version</servlet-name>
<url-pattern>/rc-av/*</url-pattern>
</servlet-mapping>
You can force the application version in web.xml :
<context-param>
<param-name>org.rcfaces.core.APPLICATION_VERSION</param-name>
<param-value>hashcode</param-value>
<!-- param-value>v10.0</param-value -->
<!-- param-value>v10.1</param-value -->
</context-param>
An URL looks like :"http://myproject/rc-av/<hashcode>/myResource.gif"
Note: If the size of resource is greater than 512ko, the AVS will automatically use gzip compression over HTTP.
Rcfaces provides a servlet to manage Css file. The first role of this servlet is to merge all the CSS files needed by an application in a single file. This limits the number of files loaded by the web browser.
The generation of this single CSS file can be a time-consuming task. To maximize the application performance, the Application Content Servlet caches the generated CSS file in memory.
Register this servlet like this :
<servlet>
<servlet-name>Rcfaces Application Contents</servlet-name>
<servlet-class> org.rcfaces.core.internal.contentStorage.ContentStorageServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Rcfaces Application Contents</servlet-name>
<url-pattern>/rc-content/*</url-pattern>
</servlet-mapping>
Another functionality of the Application Content Dynamics Servlet is to dynamically modify images.
The servlet allows to :
Change colors depending on the image state.
Change contrast and brightness for differents type of image files.
Resize images.
Convert an image to another format (JPEG, GIF, PNG and ICO).
Sample page that uses the dynamic image features of the servlet :
<v:image imageURL="images/back64.gif" />
<v:image imageURL="hover::images/back64.gif" />
<v:image imageURL="disabled::images/back64.gif" />
<v:image imageURL="selected::images/back64.gif" />
<v:image imageURL="gray::images/back64.gif" />
<v:image imageURL="contrast(0.6)::images/back64.gif" />
<v:image imageURL="brithness(-0.6)::images/back64.gif" />
<v:image imageURL="scale(0.5)::images/back64.gif" />
<!--size in px -->
<v:image imageURL="resize(50)::images/back64.gif" />
<v:image imageURL="setSize(80,150)::images/back64.gif" />
<v:imageCheckButton
imageURL="images/home.gif"
hoverImageURL="hover::"
disabledImageURL="disabled::"
selectedImageURL="selected::"
/>
<v:imageCheckButton disabled="true"
imageURL="images/home.gif"
disabledImageURL="disabled::"
/>
<!--GIF to JPEG -->
<v:image imageURL="jpeg(quality=1)::images/babelfish.gif" />
<!--JPEG to JPEG -->
<v:image imageURL="jpeg(quality=0.7)::images/babelfish.jpg" />
<!--JPEG to GIF -->
<v:image imageURL="gif::images/babelfish.jpg" />
<!--JPEG to PNG -->
<v:image imageURL="png::images/babelfish.jpg" />
<!--GIF to PNG -->
<v:image imageURL="png::images/babelfish.gif" />
Results :
original hover disabled selected gray
contrast brithness scale resize setsize
Internationalization is an important feature for a web based application. JSF standard tag <f:loadBundle> allows to perform basic internationalization. However, it isn't available for JavaScript programming.
Rcfaces client bundle servlet's goal is to make the resources bundle available for JavaScript Programming.
Register the "Rcfaces Client Bundle" servlet like this :
<servlet>
<servlet-name>Rcfaces client bundle</servlet-name>
<servlet-class> org.rcfaces.html.internal.clientBundle.clientResourceBundleServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Rcfaces Client bundle</servlet-name>
<url-pattern>/rc-cb/*</url-pattern>
</servlet-mapping>
In Faces-Config.xml, declare all the needed locales :
<application>
<locale-config>
<default-locale>en</default-locale>
<supported-locale>fr</supported-locale>
<supported-locale>de</supported-locale>
</locale-config>
</application>
Exemple :
Sample messages.properties file (in the "bundle" package) :
greeting_text=Greeting
forfun_alert=Framework created by Olivier Oeuillot
test=java is {0} and {1}
Sample JSP :
<vh:loadBundle bundleName="messages" baseName="bundle.Messages" />
<script>
var bundle=f_resourceBundle.Get("messages");
try {
alert("Bundle="+bundle.f_format("forfun_alert"));
alert("Bundle="+bundle.f_format("test", "good", "powerfull" ));
} catch (x) {
alert("Test: "+x);
}
</script>
<v:textEntry id="demoText" value="#{messages.greeting_text}" required="true">
<v:text for="demoText" text="Label : "/>
</v:textEntry>
This feature allows JavaScript access to resource bundles deployed on server.
This part describes all the parameters which can be used during the development stage.
The Javascript console is displayed inside the browser window at development time. It displays client-side messages that shows the application progress and protential errors.
To display the JavaScript console, follow these instructions :
In the web.xml, add the following lines (context parameters) :
<...web-app>
...
<context-param>
<param-name>org.rcfaces.renderkit.html.client.DEBUG_MODE</param-name>
<param-value>true</param-value>
</context-param>
<context-param>
<param-name>org.rcfaces.renderkit.html.client.ENABLE_LOG</param-name>
<param-value>true</param-value>
</context-param>
<context-param>
<param-name>org.rcfaces.core.client.DEFAULT_LOG_LEVEL</param-name>
<param-value>debug</param-value>
</context-param>
....
</web-app>
In jsp file :
<vh:javaScript requiredClasses="f_consoleAppender" />
The console is displayed in the browser window (top right corner) :
These option allows to disable caching of generated resources on the server and the client. It is useful when debugging custom javascripts and stylesheets. We strongly advice to disable caching on the client sider at development time.
| Name | Default | Description |
| org.rcfaces.renderkit.html.NO_CACHE | False | Disable cache for Stylesheet and Javascript servlet |
|
org.rcfaces.renderkit.html.JSP_DISABLE_CACHE |
False |
set tag META HTTP-EQUIV cache-control and pragma to no cache.This directive indicates cached information should not be used and instead requests should be forwarded to the origin server. And Set EXPIRES to 0 that may thus be used to force a modification check at each visit. |
|
org.rcfaces.renderkit.html.javascript.REPOSITORY_DEV_MODE |
False | Regenerate javascript repositories for each request. |
|
org.rcfaces.core.NO_CACHE |
False | Change HTTP META Tag like JSP_DISABLE_CACHE but int servlet response. |
|
org.rcfaces.core.contentStorage.DISABLE_CACHE |
False | Disable cache of the content storage servlet. |
These parameters allow to optimize the behavior of the application when you deploy them :
|
Name |
Default |
Description |
|
org.rcfaces.core.LITERAL_LOCAL |
None |
List of default supported language |
|
org.rcfaces.core.GZIP_SUPPORT |
True |
Allow Rcfaces to ZIP resources when they are biger than 512ko |
|
org.rcfaces.core.EXPIRES |
Week |
Default life time of resources in browser cache. |
|
org.rcfaces.core.VERSIONED_EXPIRES |
Year |
For versioned resources. i.e.:37w and resources expire after 37 week. |
|
org.rcfaces.core.APPLICATION_VERSION |
None |
This parameter can either take the value Hascode: URI of values are made with their hascode, or the value Now: made a random value with ce current time in millisecond, or specified value i.e.: "v1.0.3". |
|
org.rcfaces.core.ETAG_SUPPORT |
True |
Manage or not the HTTP header value Etag: this attribute is used to determine change in content at a given URL |
|
org.rcfaces.core.HASH_SUPPORT |
True |
Add the Content-MD5 entity-header field to pass through proxies and gateways |
|
org.rcfaces.renderkit.html.CONFIGURATION_VERSION |
None |
Select a specific version i.e: 1.0.3 |
|
org.rcfaces.renderkit.html.CONFIGURATION_VERSION_SUPPORT |
|
Can be set to False to forbid css version repoisitories |
|
org.rcfaces.core.RESOURCE_PROXY_DEFAULT_URL |
None |
In load balacing configuration this parameter allows to specify the default address where the application is deployed to load resources trough this application server in priority. Otherwise, a user could load file each time that they will request an other server. |
|
org.rcfaces.core.RESOURCE_PROXY_RULES_PATH |
None |
Some rules to get ressources |
|
org.rcfaces.core.ENABLE_FILTERED_RESOURCE_PROXY |
False |
Accept ressource proxy |
|
org.rcfaces.core.ENABLE_FRAMEWORK_RESOURCE_PROXI |
False |
Accept ressource proxy |
|
org.rcfaces.core.LISTENER_MANAGER_STRATEGY |
default | Allow to manage life of listener during the application wokflow. Decide when listener are added and cleaned. There are four Strategy: DEFAULT append listener at each navigation without control,CLEAN_ALL is used with facelet to remove all listener when a component is reused.CLEAN_BY_CLASS delete all listener class by class before add a new. ADD_IF_NEW add listener if it doesn't always exist. |
Web applications are all different, that's why CSS and javaScript of components can be changed (see part 11: Customize Components). Rcfaces allows to gather its own CSS or JavaScript files in bigger files (modules) to optimize loading of resources.
In the same package where you created the new CSS files (a good practice is to separate css for each component), create a XML file named repository.xml. All of our CSS files will be gathered in different modules
repository.xml example :
<?xml version="1.0" encoding="UTF-8"?>
<repository id="CssApp"
baseDirectory="com/myproject/jsf/rcfaces/css">
<module id="myCssModule">
<file name="rcfaces.css" />
<file name="button.css" />
<file name="ie_cadreRegroupement.css" />
<file name="combo.css" />
<file name="grid.css" />
<file name=grid_ie.css" userAgent="ie" />
<file name="ie_hyperlink.css" />
<file name="menuHorizontal.css" />
<file name="menuVertical.css" />
</module>
</repository>
Note: the baseDirectory attribute of the <repository> node is the package where this file is saved.
In the javascript file package container, create another file named repository.xml which looks like this :
<?xml version="1.0" encoding="UTF-8"?>
<repository id="js"
baseDirectory="com/myproject/jsf/rcfaces/js"
convertSymbols="true" >
<module id="MyJsModule">
<file name="menuHorizontal.js">
<class name="ie_menuHorizontal">
<required-class name="f_component" />
<required-class name="fa_disabled" />
<required-class name="fa_readOnly" />
<required-class name="fa_selectionManager" />
<required-class name="fa_items" />
<required-class name="fa_itemClientDatas" />
</class>
</file>
<file name="menuVertical.js">
<class name="pe_menuVertical">
<required-class name="f_tree" />
<required-class name="fa_items" />
<required-class name="fa_itemClientDatas" />
</class>
</file
[…]
<file name="messages.js">
<class name="ie_messages">
<required-class name="f_messageContext" />
<required-class name="f_messageObject" />
<required-class name="f_messages" />
</class>
</file>
</module>
</repository>
Note : JavaScript classes are instances of f_class or fa_aspect object created by the API.
The next step is to register these repositories in the Rcfaces context. In the base directory src, make a META-INF folder containing a XML file named rcfaces-config.xml with the following content:
<?xml version="1.0" encoding="UTF-8"?>
<rcfaces-config>
<repositories>
<repository>
<type>javascript</type>
< location>com/myproject/jsf/rcfaces/js/repository.xml</location>
</repository>
<repository>
<type>css</type>
<location>com/myproject/jsf/rcfaces/css/repository.xml</location>
</repository>
</repositories>
</rcfaces-config>
Finally, in a JSP file:
<header>
<vh:init />
<vh:cssStyle requiredModules="MyCssModule" />
<vh:javaScript requiredModules="MyJsModule" />
</header>
After that, stylesheets and JavaScript are integrated in Rcfaces context for having a better resource management.
Note : These tags must be inside the html <head> tag.
The page loading time must be as small as possible, and this time depends on the size of JavaScript source code the browser loads and how the functions are called.
This tag allows to gather all the <SCRIPT> HTML tags in only one. Some browser (i.e: Internet Explorer) could have a loss of performance when they have too many <SCRIPT> tag on the same page. The best way to use the <vh:javaScriptCollector> tag is :
<?xml version="1.0" encoding="UTF-8"?>
<jsp:root
version="2.0"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:jsp="http://java.sun.com/JSP/Page"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:v="http://rcfaces.org/core"
xmlns:vh="http://rcfaces.org/html"
xmlns:layout="http://jakarta.apache.org/struts/tags-tiles">
<jsp:directive.page
contentType="text/html"
pageEncoding="UTF-8" />
<layout:importAttribute scope="request"/>
<f:view>
<jsp:output
omit-xml-declaration="true"
doctype-root-element="html"
doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN"
doctype-system="http://www.w3.org/TR/html4/loose.dtd" />
<html lang="fr">
<jsp:directive.include file="_header.jspf" />
<vh:javaScriptCollector>
<body>
<h:form id="ecranGlobal">
<jsp:directive.include file="_body.jspf" />
<!-- Menu Horizontal -->
<div class="zoning_menuHorizontal">
<f:subview id="menuHorizontal">
<jsp:include page="${menuHorizontal}" flush="false" />
</f:subview>
</div>
[...]
</h:form>
</body>
</vh:javaScriptCollector>
</html>
</f:view>
</jsp:root>
There are two special versions of rc-faces-html.jar with different compilation parameters to improve size and loading time of the JavaScript runtime :
|
Version |
Size |
Loading Time |
|
|
HTML |
1 |
1 |
Free |
|
HTMLC |
0.22 |
0.6 |
Free |
|
HTMLC3 |
0.26 |
0.33 |
Premium |
These jar file will be put in the WEB-INF/lib folder of your application. They replace the rc-faces-html.jar file. We advise not to use them at development time as they make the debugging of the client-side scripts much harder. Use them at application-deployment time.
The RCFaces Javascript framework is the base of its advanced graphical user interface.
In order to create an application with RCFaces, it is important to understand the events thrown by the listeners when you select a data, expand a tree, drag an element or press a key.
When you develop an event handler, you must use the RCFaces API and the f_event object to access the page components and change their state. Use only the public APIs (mainly f_*) to avoid problems with the optimized javascript library.
7.1 How to use the object f_event in a RCFaces event.
When RCFaces throws an event, an instance of the object f_event is instanciated. This Object is created with the native javascript event object provided by the browser enriched with other informations specific to RCFaces likes: the component, the type of event, the selected item, a value and others... This event object usually provides the entry point to work with the API in a custom javascript method.
Example : opening a ViewDialog when clicking on a button
<v:viewDialog
id="idPopup"
width="450"
text="Title popup"
height="180"
visible="false"
viewURL="$context/popup/bodyPopup.jsf"
hiddenMode="client" />
<v:button text="Open popup"
selectionListener="return showViewDialog(event, 'idPopup');" />
The selection listener of this button calls the "showViewDialog" function with the javascript event object and the view dialog component id.
The "showViewDialog" function is defined in the jsp or within an external javascript file included in the page :
function showViewDialog(event, idViewDialog) {
var button = event.f_getComponent();
var viewDialog = event.f_findComponent(idViewDialog);
viewDialog.f_open(function(returnValue) {
button.f_setDisabled(returnValue);
});
return false;
}
The RCFaces event allows to find the viewDialog component (f_findComponent) and call its public f_open method. It will also change the button disabled state based on the return value of the viewDialog (here : bodyPopup.jsp). The return value is given as the second parameter of the CloseShell function.
bodyPopup.jsp:
[...]
<v:imageButton
text="Cancel"
actionListener="f_shellManager.CloseShell(event, false);return false" />
<v:imageSubmitButton
text="Validate"
actionListener="f_shellManager.CloseShell(event, true);return false" />
This example is based on a tree component with multiple levels of nodes and clientDatas.
The purpose of this example is to see how to use the functions getValue() and getItem().
These functions are often used with components which can contain items like dataGrid, comboGrid, imageCombo, tabbedPane and tree.
This is a tree in a jsp :
<v:tree id="anId"
selectable="true"
height="480"
width="620"
selectionCardinality="1"
selectedValues="#{treeBean.selectedValues}"
selectionListener="return selectTree(event);">
<f:selectItems value="#{treeBean.noeudsArbre}" />
<v:menuItem itemValue="valueTest" itemLabel="Label Test" >
<v:clientData name="IDE" value="eclipse"/>
<v:clientData name="Java" value="Is good"/>
</v:menuItem>
</v:tree>
Method selectTree :
function selectTree(event) {
var tree = event.f_getComponent();
var value = event.f_getvalue();
var label = tree.f_getNodeLabel(value);
var clientDatas clientDatas = tree.f_getItemClientDatas(event.f_getItem());
f_otherClass.f_setInfoToDoSomeThing({key: value, label: label,
clientDatas : clientDatas});
return false;
}
The event object contains the selected item as its item (f_getItem method).
The event's item may have specifics attribute, for example a tree item can be opened or not.
The f_tree class provides an item state via a specific function "f_isOpened" which takes an item as parameter.
| Name | Description |
|
f_getType() |
Returns the type of event |
|
f_getJsEvent() |
Returns the Javascript event if any |
|
f_getItem() |
Returns the item associated to the event |
| f_getComponent() |
Returns the component associated to the event |
| f_getValue() |
Returns the value of the item associated to the event |
| f_findComponent() |
Search for and return the {@link f_component} with an id that matches the specified search expression (if any).
|
| f_findSiblingComponent() |
Search for and return the sibling {@link f_component} with an id that matches the specified search expression (if any).
|
f_core is a class which has several public static functions. These functions can be used at any time in your scripts. It is possible to find a component anywhere in a page without having an event object. The following table shows several public static functions of the class f_core:
| Name | Description |
|
Assert(expr, message) |
Throws a message if the expression is true |
|
Debug (name, message, exception, win) |
add log |
| FindComponent(id, doc) |
Find a child in the main page by its identifier. (The naming separator IS ':') |
| GetElmentByClientId(id, doc, |
Find a child by its identifier. (The naming separator might not be ':') |
|
GetCookieValue (cookieName, doc) |
Returns String value associated to the cookie |
| GetDocumentSize(values, doc) |
Returns the size of the document |
| GetViewSize(values, doc) |
Returns the size of the View |
|
GetViewPosition (doc) |
Returns the position of the Window |
| SetFocus(component, asyncMode) | give focus to a component |
|
ShowVersion() |
return RC-Faces version |
|
ValidateForm(component) |
valide form of the component in parameter |
To call these static public functions, use this syntax: f_core.GetElementByclientID.
When you develop your own components and associated classes, you are entitled to use hidden functions like : CreateElement, GetAttribute or GetJsEvent. Theses functions may not be used in standard page scripts (they will not be available at runtime).
Each component is linked to a javascript class. During a development some components can be created or other already existing can evolve therefore it is important to know how to create a RCFaces javascript class.
The RCFaces javascript API relies on an original architecture which allows to create classes that can inherit from another class and also use "aspects" thanks to the Prototype framework.
Each class can have a static part and a member part with class attributes and methods.
Example :
/**
* f_scheduler class
*
* @class public f_scheduler extends f_component, fa_items, fa_selectionManager
* @author jbmeslin@vedana.com
* @version $Revision: 1.0
*/
var __statics = {
/**
* @field private static final String
*/
_PERIOD_STYLE: "f_scheduler_period",
/**
* @method private static
* @param Event event
* @return boolean
* @context object:scheduler
*/
_OnPeriodMouseOver : function(evt) {
var scheduler = this._scheduler;
if (!evt) { //pour IE
evt = f_core.GetJsEvent(this);
}
scheduler.f_update();
[...]
}
};
var __members = {
//constructor
f_scheduler : function() {
this.f_super(arguments);
this._selectionCardinality=fa_cardinality.OPTIONAL_CARDINALITY;
},
//destructor
f_finalize : function() {
this.f_super(arguments);
this._anObject = undefined;
this._aListener = null;
//this._primitifType = undefined
},
/**
* @method public
* @param Object item
* @return void
*/
f_addPeriod: function(item) {
item._begin = f_core.DeserializeDate(item._begin);
item._end = f_core.DeserializeDate(item._end);
this.f_addItem(this, item);
},
/**
* @method protected
* @return void
*/
f_update: function() {
[...]
},
/**
* @method protected
* @return void
*/
fa_updateElementStyle: function(divNode) {
[...]
},
/**
* @method private
* @return Object
*/
f_aPrivateMethod: function() {
[...]
}
};
new f_class("f_scheduler", {
extend : f_component,
aspects : [ fa_items, fa_selectionManager ],
statics : __statics,
members : __members
});
In this example, the class f_scheduler extends f_component and has two aspects: fa_items and fa_selectionManager.
Static and member variables are declared in the two __statics and __members variables. The call of "new f_class()" initialize the f_scheduler class declaration : inheritance, aspects, static and member parts.
When a JSF component is rendered in a page, the javascript framework will create an instance of this new class. This object is associated with the HTML component.
The new javascript object can be used inside event handlers. You must only use the public component methods and properties and not try to access directly the HTML component. The framework use the @method annotation to know which methods are public. The JSDoc documentation tool will also use the @... annotations when building the component's documentation.
The service component is a non-visual component, it allows to call server-side code from javascript thanks to the AJAX service. A POJO method, which is register as a managed bean can be invoked in synchronous or asynchronous mode without JSF page navigation.
The service component can be declared at any time in the JSP and several service tags can be declared in the same page.
The following example performs an AJAX based "Hello world" :
JSP :
<v:service
id="myAjaxService"
serviceEventListener="#{myBean.getResults}" />
<v:button text="Call Service"
selectionListener="return callService(event, 'myAjaxService',
'resultService','world');" />
<v:text id="resultService" />
The id attribute of the service tag allows to find the service component with the javaScript RCFaces API. The "serviceEventListener" attribute allows to bind the service with a method of a managed bean.
Here is the java method that builds the sentence:
public String getResults(ServiceEvent event) {
Map<String, String> data = (Map) event.getData();
String param = data.get("param2"); //setted in the js function callService
return "Hello " + param ;
}
The ServiceEvent parameter is required. It is a specialized FacesEvent.
This java object can provide additional parameters based on its data attribute which can be a String or a Map like in this example. This map was created in the function "callService" described below.
Now that we have the service tag and the java bean, the next step is to write the javaScript function which creates the link between them.
Even if AJAX is basically asynchronous, the service component can do a synchronous request like this :
callService: function(event, idService, idResult, otherParam) {
var servComp = event.f_findComponent(idService);
if (servComp) {
var result = servComp.f_syncCall({param1:idResult, param2:otherParam});
var resComp = f_core.GetElementByClientId(idResult);
if (resComp) {
resComp.f_setText(result);
}
}
}
The managed bean is called by the function f_synCall which return the result of the java method declared in the serviceEventListener attribute.
You should be aware that using this mode with Firefox will block the user interface.
The asynchronous mode is the preferred mode and is more natural in AJAX. The service component still calls the serviceEventListener, but instead of returning the result directly, a callback function will be called :
callService: function(p_Event, idService, idResult, otherParam) {
var servComp = p_Event.f_findComponent(idService);
if (servComp) {
servComp.f_asyncCall(myCallBack ,//myCallBack is a function
{param1:idResult, param2:otherParam});
}
}
The function f_asyncCall take a callback function as its first parameter. It will be the function called by the ajax engine. The second parameter is a map that is used to send parameters to the server side code, these parameters are also provided to the callback function.
The last step with asynchronous mode is to get the response within the callback. The callback function must have three parameters : a status to know if the work is done or in progress, a map of parameter (The map put in f_asyncCall) and the result object.
The example function source code:
myCallback: function(status, params, result) {
if(status!=f_service.LOADED_STATE) {
return;
}
var resComp = f_core.GetElementByClientId(params['param1']);
if (resComp) {
resComp.f_setText(result);
}
}
Type of response setted in the param result can be a primitive type or any type of object.
Client validators are used in web forms to check and validate user entry in specific fields which either must respect some rules or have a format or must be converted before sending data to the server. It is mostly used to check fields like Email , credit card, password, phone number, etc...
A Client Validator is an attribute of the TextEntry component. The validator is a piece of JavaScript code that is called when a component lose its focus or when it changes its value. The validator is also called when a form is submitted on the server side during the generate response steps of JSF cycle.
A validator consists of fiyrdifferent parts :
Filter: validate each character with a regular expression.
Translator: Format each character i.e: upper case, see if separator is accepted or replace it.
Checker: Check validity of data. For exemple checking syntax of an Email.
Formatter: Apply specifc pattern of the field i.e : format a date according to the current locale.
For these four parts are implemented as a javascript function called on the client side and a java class on the server side when the form is submitted by the browser. The browser side generate the formatted field value in the "Render" phase of the JSF lifecycle. A validator can't have several filter, translator, chercker, or Formatter.
Presently several Client Validator are available, it is possible to change their behaviors and their parameters. Moreover it is easy to create new validators using some part of existing validators or new validator parts.
We advise you to look at the rcfaces-config.xml file contained in rcfaces-html.jar to see all existing client validators. Extract of rcfaces-config.xml :
<clientValidator id="number">
<parameter name="num.signed" value="false" />
<parameter name="num.decimal" value="0" />
<parameter name="num.negSign" value="-" />
<parameter name="num.decSign" value=",." />
<parameter name="num.sepSign" value=" " />
<filter call="f_vb.Filter_num" class="org.rcfaces.core.internal.validator.impl.NumFilter"/>
<translator call="f_vb.Translator_num"
class="org.rcfaces.core.internal.validator.impl.NumTranslator"/>
<checker call="f_vb.Checker_num" class="org.rcfaces.core.internal.validator.impl.NumChecker"/>
<formatter call="f_vb.Formatter_num" class="org.rcfaces.core.internal.validator.impl.NumFormatter" />
<converter object="f_vb.Converter_num" />
</clientValidator>
As shown in this example, a client validator may use a converter to convert a string field to an object and vice versa.
| Name | Description | Parameters |
| noblank | No white space |
|
|
scientific |
Check number scientific : [0-9\.\-eE] |
|
|
money |
Check money field: [0-9\.\+\-] |
|
| code |
entering a listing: [a-zA-Z] |
|
|
alpha |
entering a listing: [a-zA-Z] |
alpha.otherChars |
|
number |
seizure of any number | num.signed, num.decimal, num.negSign, num.decSign, num.sepSign |
|
card |
card number: [0-9\.] |
|
|
name |
seizure of name: [ a-zA-Z0-9\*\.\-] |
|
|
removeaccent |
replace all accented characters |
|
|
skipBlank |
remove blank at the begin and the end of String |
|
|
alpha_fr |
allow all french charaters |
alpha.otherChars |
|
alpha_fr_upper |
replace alphabetic characters and accented characters in capital (Uppercase) |
|
|
alpha_fr_lower |
replace alphabetic characters and accented characters in lowercase |
|
|
digit |
Check number : [0-9] |
|
|
alpha_digit |
Allow alphabetic charaters and digit [ a-zA-Z0-9] |
|
|
upper |
Performing the translation validation of all characters wuth uppercase equivalent |
|
| lower |
Performing the translation validation of all characters wuth lowercase equivalent |
|
|
dps |
allow only the ACII printable characters (32-127) |
|
|
insee |
check diget and alfa and translate in the INSEE format |
|
|
date |
Check Date |
|
| Check validity of an e-mail |
|
Just for fun, the aim of this exercise will be to create a client validator which does a Caesar cipher.
When the user enters a character, the validator filters the letters of alphabet, then change to upper case and finally calls the formatter to do cipher algorithm.
First In the base source directory of your project (src), create a META-INF folder and a rcfaces-config.xml file in this folder. Paste the following text :
<?xml version="1.0" encoding="UTF-8"?>
<rcfaces-config>
<repositories>
<repository>
<type>javascript</type>
<location>org/rcfaces/rcfacesformsample/js/repository.xml</location>
</repository>
</repositories>
<clientValidators>
<render-kit>
<!--
/** * Validaor CAESAR * * Caesar cipher*/
-->
<clientValidator id="CAESAR">
<parameter name="nb.shift" value="5" />
<!-- if is letter -->
<filter call="f_vb.Filter_alpha"
class="org.rcfaces.core.internal.validator.impl.AlphaFilter" />
<!-- To upper case -->
<translator call="f_vb.Translator_uppercase"
class="org.rcfaces.core.internal.validator.impl.UpperCaseTranslator" />
<!-- Caesar cipher -->
<formatter call="av_caesar.DoIt" />
</clientValidator>
</render-kit>
</clientValidators>
</rcfaces-config>
The parameter named "nb.shift" allows to change the shift of the cypher. The filter phase of this validator calls a javascript function named Filter_alpha and its java class which do the same.
Tanslator calls a Rcfaces javascript function named Filter_alpha and its java class which do the same formatting.
Then formatter calls a new function wich must added in a repository.xml file :
<?xml version="1.0" encoding="UTF-8"?>
<repository id="javascript" baseDirectory="org/rcfaces/rcfacesformsample/js" convertSymbols="true">
<module id="clientValidator">
<file name="av_caesar.js">
<class name="av_caesar">
</class>
</file>
</module>
</repository>
The av_caesar.js file :
/**
* class av_caesar.
*
* @class av_caesar
* @author JBM
*
*/
var __statics = {
/**
* @method public static
* @context object:validator
*/
DoIt : function(validator, inVal) {
if (!inVal) {
validator.f_setObject(null);
return inVal;
}
k = validator.f_getIntParameter("nb.shift");
Alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ';
var clair = inVal;
while (k < 0) {
k += 26
};
while (k > 25) {
k -= 26
};
n = 0;
chiffre = "";
for ( var count = 0; count < clair.length; count++) {
var alpha = clair.charAt(count);
idx = Alphabet.indexOf(alpha);
if (idx > -1) {
if ((n % 5 == 0) && (n > 0)) {
chiffre += " ";
};
n++;
chiffre += Alphabet.charAt(idx + k);
}
}
return chiffre;
}
}
new f_class("av_caesar", {
statics : __statics
});
Finally, we create a jsp file that use the new custom validator :
<vh:javaScript requiredModules="clientValidator" />
<v:textEntry id="field" clientValidator="CAESAR">
When the user type within the text field, only alpha characters are authorized. They are automatically changed to upper case as the user is typing. When the field loses focus, the Caesar cipher is called.
Since the 1.2 version, RCFaces allows to perform Drag & Drop operations with the Tree and the DataGrid components. Only those two components are available for D&D as of today, but it should be expanded to other components in the future.
Example :
<v:tree draggable="true"
dragTypes="text/*"
dragEffects="copy"
droppable="true"
dropTypes="text/*, app/object"
dropEffects="copy"
dropCompleteListener="return serviceOrBean();#{aCleverBean.doDndInData}">
...
</v:tree>
By default the dnd effect value is : "default" but RCFaces allows to define other accepted dragEffects and/or dropEffects with values: none, default, any, copy, move or link. During a default drag behaves as a file manager drag : Drag + CTRL to copy, Drag + MAJ to move and Drag + ALT to create a link. The default dragTypes is "x-RCFaces/xxx" where xxx can be a tree node or grid row. The default dropTypes is "*/*", It is possbile to drop any type of element.
Items contained inside the Tree and the DataGrid components inherit these dnd properties.
It is always possible to specify a different behavior for each item. drag/dropEffects and drag/dropTypes attributes are available on all the different types of items component.
For example, we can build a tree whose some branches accept to "copy" some apples and other branches accept to "move" some pears.
By default to drop an element you must be over another item. But RCFaces allows to drop new element on the component body with the Boolean attribute bodyDroppable.
The developer decides how and where a dragged item will be handled in relation to the item which has thrown the drop event (function called by the dropCompleteListener on DROP_COMPLETE event).
Three Listeners and a common event object may be used by the developer to control the behavior of RCFaces Drag & Drop :
The dragListener handle events thrown when an element is caught with a mousedow event and the mouse begin to move. Conversely the dropListener is called when an dragged element is passing over another element and a "mouseup" event is handle. These two listeners are not mandatory, but they allow to pass additional information to change icon, cursor, add some text in order to customize your own D&D to provide feedback to the final user.
The third listener and the most important is the dropCompleteListerner. Most of the time its goal will be to make an AJAX request to update the components on server side and reload them graphically via a javascript function. Developers can bind a managed bean method to this listener and make a JSF navigation like in other actions.
The common point between these three listerners is the f_dndEvent object. This object allows to get the source and the target item together with component container, effects, types, get the stage of dnd and an instance of the javaScript Drag&drop engine. To get the engine instance, use the Static js function f_dndEvent.As(event) :
function myDrag(event) {
var targetTree=event.f_getComponent();
var dndEvent=f_dndEvent.As(event);
switch(dndEvent.f_getStage()) {
case f_dndEvent.DRAG_START_STAGE:
var infos=new Object();
infos[f_iconDnDInfo.DND_SOURCE_IMAGE]="images/iconDnD.jpg";
infos[f_iconDnDInfo.DND_SOURCE_IMAGE_WIDTH]=48;
infos[f_iconDnDInfo.DND_SOURCE_IMAGE_HEIGHT]=48;
dndEvent.f_getDragAndDropEngine()
.f_setSourceAdditionnalInformations(infos);
}
}
or :
function myDropComplete(event) {
var dndEvent=f_dndEvent.As(event);
alert("OK ! from="+dndEvent.f_getSourceItemValue()+
"to="+dndEvent.f_getTargetItemValue());
return true ;
}
For more information about what is possible, refer to the javascript doc of the following classes :
You can also download the dnd project example.
There are twelve stages possible in D&D. It is important to know them in order to understand why a dnd failed and when the dnd is finish.
Drag and Drop activity diagram :
In this diagram all yellow diamonds (type decision or merge) translate to RCFaces event type ""DRAG" containing the current stage.
These stages can be intercept in drag/drop/dropCompleteListener.
The blue diamonds represent the basic user input to do drag and drop : a mouse click down to drag an element, then slides over the other items and drop the element.
The red diamonds is the lastest event thrown by the API to call a JSF navigation.
Rcfaces allows to add different complex components in a web application. These components must integrate properly with skins and behaviors of the application, that's why each component have many CSS classes which can be overriden. Besides that skin customisation, Rcfaces also allows to override Java renderer and javascript file for every component.
Using style-sheets is the quick and easy way to customize a component. All the Rcfaces components have a StyleClass attribute that can be used to change their CSS class.
i.e : change TextEntry compoent CSS class. In a jsp file :
<v:textEntry id="demoText" required="true" styleClass="test_class" />
In a CSS file (referrenced by the JSP files) :
.test_class .f_textEntry {
border:1px solid #ABA6A1;
color:#333333;
font-family:Verdana;
font-size:11px;
font-weight:bold;
}
.test_class .f_textEntry_error {
background-color:#FFCCCC;
}
Here ".f_textEntry" is the CSS class defined by component renderer. Moreover, in this example the "required" attribute can change the component's state to error, so there is CSS class for this state : ".f_textEntry_error".
The CSS classes of every component have a suffix for all possible states i.e : "_selected", "_disabled", "_readOnly" ...
Every component part alos have a suffix such as "_title" to specify title label or "_topLeft", "_middle", "_topRight" to specify images of a rounded component or "_tab" in the case of a tabbed pane.
Note : To retrieve these suffixes there are some tools like Firebug for Firefox, Internet developer toolbar and Debugbar for IE.
Components are continually evolving so it is possible that CSS subclasses change from version N to version N+1.
Sometimes it is possible to create components based on the same interface but with a very different look and feel. When changing the stylesheets is not enough, you can create a "lookid" for this component. A lookid consists of a specific java class that performs the rendering phase of the JSF lifecycle, the class must perform the encoding and decoding of the component for the user device. The rendering phase emits HTML code based on the component attributes.
By writing a new renderer, you can freely specify :
The generated HTML tags that make the component on the client side
The CSS classes used by a component (f_*)
The JavaScript code used by a component.
A Rcfaces component has two types of rendering, one that is called on the server side and one that is called on the client side (javascript code). API consistency must be enforced at all time.
There are at least four methods to override from AbstracCssRenderer:
getMainStyleClassName(): return the css class name.
getJavaScriptClassName(): return the js class name. If no css class name is defined this class name become the default css class name.
encodeBegin(IComponentWriter writer): create HTML Tag thanks a writer.
encodeEnd(IComponentWriter writer): create HTML Tag thanks a writer.
Example of overriding a Renderer :
public class TabbedPaneRenderer extends CardBoxRenderer {
protected String getJavaScriptClassName() {
return JavaScriptClasses.TABBED_PANE;
}
protected String getMainStyleClassName() {
return "ie_tabbedPanne";
}
protected void encodeBegin(IComponentWriter writer) throws WriterException {
// call parent methode
super.encodeBegin(writer);
//get component context
IComponentRenderContext componentRenderContext = writer
.getComponentRenderContext();
//get componen
CardBoxComponent tabbedPaneComponent = (CardBoxComponent)
componentRenderContext.getComponent();
//getFacesContect
FacesContext facesContext = writer.getComponentRenderContext()
.getFacesContext();
//get Component Attribute thanks facesContext
String text = tabbedPaneComponent.getToolTipText(facesContext);
String height tabbedPaneComponent.getHeight(facesContext);
//use an htmlWriter
IHtmlWriter htmlWriter = (IHtmlWriter) writer;
//start write html tags component
htmlWriter.startElement(IHtmlWriter.UL);
writeHtmlAttributes(htmlWriter);
writeJavaScriptAttributes(htmlWriter);
writeCssAttributes(htmlWriter)
writer.startElement(IHtmlWriter.LI);
writer.writeClass(getMainStyleClassName()+ "_left");
writer.endElement(IHtmlWriter.LI);
writer.startElement(IHtmlWriter.LI);
writer.writeClass(getMainStyleClassName()+ "_mid");
[…]//write Label for example
writer.endElement(IHtmlWriter.LI);
writer.startElement(IHtmlWriter.LI);
writer.writeClass(getMainStyleClassName() + "_right");
writer.endElement(IHtmlWriter.LI);
[…]//call other function like doHeader
}
protected void encodeEnd(IComponentWriter writer) throws WriterException {
IHtmlWriter htmlWriter = (IHtmlWriter) writer;
htmlWriter.endElement(IHtmlWriter.UL);
super.encodeEnd(writer);
}
}
This example is just an overview, before you start to do your own component renderer, it is advisable to understand carefully how the component works and read its source code.
If you the HTML structure, the javaScript class associated to a component must be overriden and registered in a JavaScript module (see Module Parameter).
Note: Some Components which contain Select Item work with a Decorator like Combo, Tree or calendar ... for this type of component, HTML tags are written in the decorator class.
The Rcfaces API allows to create classes with variable members, statics and aspects. It is possible to extend exisiting JavaScript classes. The followings source code is just an overview that shows how a extend an existing JavaScript class.
ie_tabbedPane.js :
/**
* @class public ie_tabbedPane extends f_cardBox
*/
var __statics = {
[…]
/**
* @method private static
* @param Event
* evt
* @return boolean
* @context object:tabbedPane
*/
_TabbedPane_mouseout: function(evt) {
var tab=this._tab;
var tabbedPane=tab._cardBox;
if (!evt) {
evt = f_core.GetJsEvent(this);
}
if (tabbedPane.f_getEventLocked(evt, false)) {
return false;
}
tabbedPane._tabMouseOut(this._tab, evt);
return f_core.CancelJsEvent(evt);
}
[…]
}
var __members = {
//constructor
ie_tabbedPane: function() {
this.f_super(arguments);
this._tabIndex=f_core.GetAttribute(this, "v:tabIndex");
},
//destructor
f_finalize: function() {
this._overTab=undefined; // f_tab
this.f_super(arguments);
},
[…] //see source code
/**
* @method protected
* @param f_tab tab
* @return void
*/
f_updateCardStyle: function(tab) {
var textTitle;
var textTitleLeft;
var textTitleMid;
var textTitleRight;
var className = "ie_tabbedPane";
if (this._selectedCard==tab) {
textTitle = className+"_UL "+className+"_selected_UL";
textTitleLeft =className+"_left "+className+ "_selected_left";
textTitleMid = className+"_mid "+className+ "_selected_mid";
textTitleRight =className+"_right "+className+
" _selected_right";
}else {
[...]
}
if (tab._disabled) {
textTitle =className+" _UL "+className+ " _disabled_UL";
textTitleLeft = textTitleLeft =className+" _left "+className+
"_disabled _left";
textTitleMid = className+" _mid "+className+ " _disabled_mid";
textTitleRight = className+" _right "+className
+" _disabled _right";
}
tab._textTitle.className=textTitle;
tab._rightTitle.className=textTitleRight;
tab._leftTitle.className=textTitleLeft;
tab._midTitle.className=textTitleMid;
},
[...]
}
new f_class("ie_tabbedPane", {
extend: f_cardBox,
members: __members,
statics: __statics
});
Add this .js file in your own javaScript repository (see part 6.4.2 Create a JavaScript repository)
After these previous steps, the renderer must be registered. Add the following lines to rcfaces-config.xml :
<renderer>
<component-family>rcfaces</component-family>
<renderer-type>org.rcfaces.core.tabbedPane:ie_tabbedPane</renderer-type>
<renderer-class>com.project.renderer.TabbedPaneRenderer</renderer-class>
</renderer>
The ie_tabbedPane suffix signal that this renderer is a Rcfaces LookId.
Example JSP file :
<v:tabbedPane lookId="ie_tabbedPane"
width="750" height="555">
<v:tab prependId="true" lookId="ie_tab" text="Titre">
<jsp:directive.include file="ongletBody1.jspf" />
</v:tab>
<v:tab prependId="true" lookId="ie_tab" text="Titre 2">
<jsp:directive.include file="ongletBody2.jspf" />
</v:tab>
</v:tabbedPane>
In Css File javascript class like "*f_" are replaced by "ie_*" :
@charset "UTF-8";
.ie_tabbedPane {
border-bottom: 0;
display: block;
}
.ie_tabbedPane_content {
-moz-box-sizing: border-box;
box-sizing: border-box;
border: 1px solid #ABA6A1;
border-top: 0;
padding: 10px;
display: block;
xheight: 531px;
font-size: 11px;
}
.ie_tabbedPane_left{
background: transparent url("tab_left.gif") left bottom no-repeat;
width: 6px;
}
.ie_tabbedPane_mid {
background: transparent url("tab_mid.gif") left bottom repeat-x;
line-height: normal;
font-family: Verdana;
font-size: 13px;
color: #333333;
font-weight: normal;
}
.ie_tabbedPane_right {
background: transparent url("tab_right.gif") left bottom no-repeat;
width: 11px ;
}
.ie_tabbedPane_selected_left {
background: transparent url("tab_selected_left.gif") left bottom no-repeat;
width: 5px;
}
.ie_tabbedPane_selected_mid {
border-top: 1px solid #ABA6A1;
background: white;
font-family: Verdana;
font-size: 13px;
color: #333333;
font-weight: bold;
line-height: normal;
}
.ie_tabbedPane_selected_right {
background: transparent url("selected_right.gif") left bottom no-repeat;
width: 13px ;
}
.ie_tabbedPane_title {
background: transparent url("tab_header.gif") right center repeat-x;
height: 34px;
float: none;
}
.ie_tabbedPane_tab {
-moz-box-sizing: border-box;
box-sizing: border-box;
display: block;
overflow-y: auto;
overflow-x: hidden;
xwidth: 738px;
xheight: 510px;
}
A good practice is to do a ie_tabbedPane.css and add it in a Css repository (see part 6.4.1 Create a CSS repository )
The ability to access information and service for people with disabilities is an important issue today. In many countries, this has led to initiatives, laws and regulations that aim toward providing easier access to web content.
Today, RCFaces is used by a french public organisation for which accessibility is required. That's why we started to implement the WAI-ARIA specification in RCFaces.
For each component the role attribute is defined. Developers can specified a different text with the arialLabel Attribute which generate the value of aria-label property. The hierarchical level of an element can be modified with the ariaLevel Attribute.
The others ARIA attributes like aria-activedescendant/poseinset/expanded/disabled/selected or labelledby are generated in component's renderer.
WAI-ARIA is implemented on the more used components, tested with Jaws by a professional end-user. For more information or development request contact us at info@vedana.com