In this tutorial we will dig into the underlying mechanism that one of the tag(<a4j:support>) that Richfaces use to Ajax enable one of the standard JSF component (<h:inputText>).
1) Create a default JSF1.2, Facelets, Richfaces 3.2.0 project using the Jboss Tools
plugin for eclipse.
2) The default project will have an empty “JavaSource” project but the “WebContent”
folder will have two files, one is index.jsp and the other is start.xhtml.
3) Right click default project and click Run As > Run on Server. The index,jsp
file will be loaded and it forwards to start.jsf, which will be handled by Faces
Servlet. Depending on the context param “javax.faces.DEFAULT_SUFFIX” (default is
.xhtml), Faces Servlet will start to look for an extension (default is .xhtml)
file with name “start” and whatever inside start.xhtml will be loaded.
4) Now we are going to modify start.xhtml to include a <h:outputText> (which
will by default render as HTML <span> element) and <h:inputText>
(which will render as HTML <input> element) and using a very important
richfaces tag called <a4j:support> ( render as a JavaScript function that
makes Ajax submit ) we are going to Ajax-enable the <h:inputText>. What it
means is that ,whenever any key is pressed in the input box we can send the
latest value in the text box to the server without refreshing the page, by using
Ajax submit.
5) The xhtml code is given below:
< !DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:rich="http://richfaces.org/rich"
xmlns:a4j="http://richfaces.org/a4j">
<head>
<title>RichFaces< /title>
< /head>
<body>
<a4j:form>
<h:outputText value="You have typed #{start.inputValue}" id="outText"/><br/>
<h:inputText value="#{start.inputValue}">
<a4j:support actionListener="#{start.handleInput}"
event="onkeyup" reRender="outText" />
< /h:inputText>
< /a4j:form>
< /body>
< /html>
6) The backing bean code is given below:
public class Start {
public Start() {
}
private String inputValue;
public String getInputValue() {
return inputValue;
}
public void setInputValue(String inputValue) {
this.inputValue = inputValue;
}
public void handleInput(ActionEvent evt){
System.out.println("Input value received at the server side is " +evt.getComponent().getParent().getValueExpression("value").getValue(FacesContext.getCurrentInstance().getELContext()));
}
}
7) The faces-config.xml code is given below:
< ?xml version="1.0" encoding="UTF-8"?>
< faces-config version="1.2" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xi="http://www.w3.org/2001/XInclude"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd">
< managed-bean>
< managed-bean-name>start< /managed-bean-name>
< managed-bean-class>Start< /managed-bean-class>
<managed-bean-scope>request< /managed-bean-scope>
< /managed-bean>
<application>
<view-handler>com.sun.facelets.FaceletViewHandler< /view-handler>
< /application>
< /faces-config>
8) The screenshot of the page is given below:
a) Whatever user types on the input text is reflected back on the output text without the page refreshing.
b) The corresponding console log at the server side – It shows that on every keyup the entered letter is sent to the server and returned back to be shown on the output text.
9) The actual HTML rendered to the browser is given below:
<form id="j_id2" name="j_id2" method="post" action="/sampleProj/start.jsf">
<span id="j_id2:outText">The user has typed < /span><br />
<input type="text" name="j_id2:j_id4"
onkeyup="A4J.AJAX.Submit('_viewRoot','j_id2',event,{'parameters':{'j_id2:j_id5':'j_id2:j_id5'} ,'actionUrl':'/sampleProj/start.jsf'} )" />
<input type="hidden" name="j_id2" value="j_id2" />
<input type="hidden" name="autoScroll" value="" />
<input type="hidden" name="j_id2:j_idcl" value="" />
<input type="hidden" name="j_id2:_link_hidden_" value="" />
<script type="text/javascript">
function clear_j_id2() {
_clearJSFFormParameters('j_id2','',['j_id2:j_idcl','j_id2:_link_hidden_']);
}
function clearFormHiddenParams_j_id2() {
clear_j_id2();
}
function clearFormHiddenParams_j_id2() {
clear_j_id2();
}
clear_j_id2();
< /script>
<input type="hidden" name="javax.faces.ViewState" id="javax.faces.ViewState" value="j_id1" />
< / form>
<?xml version="1.0"?>
<html xmlns="http://www.w3.org/1999/xhtml">
<head><title></title>
<link type="text/css" rel="stylesheet" href="/sampleProj/a4j_3_2_0-SNAPSHOTorg/richfaces/renderkit/html/css/basic_classes.xcss/DATB/eAHblDspFQAFnAIX.jsf" class="component" />
<link type="text/css" rel="stylesheet" href="/sampleProj/a4j_3_2_0-SNAPSHOTorg/richfaces/renderkit/html/css/extended_classes.xcss/DATB/eAHblDspFQAFnAIX.jsf" class="component" />
<script type="text/javascript" src="/sampleProj/a4j_3_2_0-SNAPSHOTorg.ajax4jsf.javascript.AjaxScript.jsf"></script>
<script type="text/javascript" src="/sampleProj/a4j_3_2_0-SNAPSHOTorg/ajax4jsf/javascript/scripts/form.js.jsf"></script>
</head>
<body>
<span id="j_id2:outText">You have typed Richfaces Rocks!!!</span>
<meta name="Ajax-Update-Ids" content="j_id2:outText" />
<span id="ajax-view-state">
<input type="hidden" name="javax.faces.ViewState" id="javax.faces.ViewState" value="j_id5" />
</span>
<meta id="Ajax-Response" name="Ajax-Response" content="true" />
</body>
</html>
11) From the above code we can extract lot of information about the way JSF and Richfaces works:
a) id="j_id2" is the id of the <form> generated by JSF if the developer doesn’t give one. This means that almost all HTML elements will have an unique “id” (either developer provided or JSF provided).
b) id="j_id2:outText" is the id of the <span> element inside the parent container - the <form> element. The id of the contained element will have the id of the parent element prepended to it, separated by semicolon.
c) The final piece of the puzzle that makes an ordinary <h:inputText> tag into Ajax enabled one is the ‘onkeyup’ event handler of its rendered <input> element, onkeyup="A4J.AJAX.Submit (…)”. <a4j:support> tag takes whatever value we give to it’s “event” attribute and place it as an attribute on its parent tag’s rendered element. So, <a4j:support event = ‘onkeydown’ …/> will add onkeydown=”A4J.AJAX.Submit(…)” attribute on it’s parent tag’s rendered element which is <input> html element.
12) A4J.AJAX.Submit is the bootstrap function inside AJAX.js file from the Richfaces library that initiates the Ajax call to the server. Few comments on its inner working is given below:
a) A4J.AJAX.Submit = function( containerId, form, evt , options ) {
…
/* The ’event’ object that is originally passed to Submit function is cloned (made a copy of) for later use. Some of the important properties from ‘event’ object that are needed to be store for later use are ‘target’, ‘srcElement ‘, ‘type’. The second parameter of the CloneObject function specifies if we should skip to clone ‘functions’ of the event object. The default is to clone everything.*/
domEvt = A4J.AJAX.CloneObject(evt,false);
…
A4J.AJAX.SubmitRequest( containerId, form, domEvt , options );
}
b) A4J.AJAX.SubmitRequest = function( containerId, formId ,domEvt , options ) {
. . .
/* If the form has any onsubmit function then it calls it */
if(!options.submitByForm && form && form.onsubmit) {
if( form.onsubmit() == false ){
return false;
};
};
. . .
/* The below code will take the options object and look for the parameters key and get its value and prepare the query parameters from it. */
if(options.parameters){
tosend.appendParameters(options.parameters);
};
. . .
/* setting the onready event handler function */
req.onready = A4J.AJAX.processResponse;
. . .
/* Function inside which actual Ajax request with form parameters is prepared and sent to the server.*/
req.send();
return false;
}
. . .
var ajaxResponse = req.getResponseHeader('Ajax-Response');
. . .
if( ajaxResponse != "true"){
. . .
} else {
/* From the Ajax response (step 9), 'Ajax-Response' meta tag value is ‘true’, so the code reaches the else part. */
/* Richfaces internally uses Sarissa javascript library for few Ajax functionalities. Sarissa is an ECMAScript library acting as a cross-browser wrapper for native XML APIs. It offers various XML related goodies like Document instantiation, XML loading from URLs or strings, XSLT transformations, XPath queries etc and comes especially handy for people doing what is lately known as “AJAX” development */
if(req.getParserStatus() == Sarissa.PARSED_OK){
. . .
/* This below code gets all the DOM element id’s that needs to be updated on the client side with the new values form the server. This is where the magic of partial page update of Ajax enabled JSF applications is realized. From the Ajax response (step 9), ‘Ajax-Update-Ids’ meta tag value is ‘j_id2:outText’ which is exactly equivalent to the ‘reRender’ attribute of
var idsFromResponse = req.getResponseHeader("Ajax-Update-Ids");
. . .
} else if( idsFromResponse != "" ) {
. . .
var childs = idsFromResponse.split(",");
for ( var k=0 ; k < childs.length ; k++ ) {
var id = childs[k];
if ( id ) {req.updatePagePart(id, k==childs.length-1);};
};
}
. . .
}
}
/* Get the new element form the Ajax Response by ‘id’.
From the Ajax response (step 9), the new element for id ‘j_id2:outText’ is You have typed Richfaces Rocks!!!
*/
var newnode = this.getElementById(id);
// The below is the oldnode that needs to be replaced with the
new node.
var oldnode = window.document.getElementById(id);
. . .
/* The below line actually replaces the old element with new element for all browsers except opera 8 (which is done in a different way).*/
oldnode.outerHTML = new XMLSerializer().serializeToString(newnode);
. . .
/* If there is any script inside the new element then that is executed using eval(script) or execScript(script).*/
this.evalScripts(newnode, isLast);
. . .
}
No comments:
Post a Comment