Communicating with JSF Bean through Javascript using jsf.ajax.request

In web development, many a times we need a way to interact with the server side from the client tier. One standard way which I've been using is through the use of Ajax, and when it comes to JSF and in particular JSF 2.0, Ajax is the standard here too. But sometimes we might want to make Ajax calls to the server side and have the results returned and use it the way we want.

JSF 2.0 already gives standard ways of doing what I am trying to show here, but I found this way quite helpful sometimes e.g. if I have two or more forms and from one of the forms I want to keep on saving the data as I change but I dont to keep that functionality just applied to that form alone but make it generic.

Other case could be that if you have Custom component written say in JSP or using JSF 2.0 Custom Component Standard way but say if you have basic HTTP elements used in those custom components, then you might want to have a way to communicate back to the JSF Bean and use the data as if you using it through JSF.

Other case could be that if you have created custom component using jQuery JS library alone, and in that case also it's highly likely that we would need data coming from server and might also need to send back.

Here I will take a very basic XHTML file which has two input elements in one form and there is one more form which has one simple Html button and one JSF hidden component.




<ui:composition lang="en"
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:c="http://java.sun.com/jsp/jstl/core"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:p="http://primefaces.org/ui"
    xmlns:my="http://java.sun.com/jsf/composite/mycomponents"
    xmlns:util="http://java.sun.com/jsf/composite/util">

<h:form id="mainForm">
       <h:inputText id="id" value=""/>
<h:inputText id="outText22" value="" />
    </h:form>

<h:form prependId="false" id="hiddenForm">
  <input type="button" id="SubmitHiddenFormButtonId" value="SubmitHiddenForm" onclick="submitHiddenForm();"/>
  <h:inputHidden id="idHidden1" value="#{register.hiddenValue}" valueChangeListener="#{register.valueChangeListener}"/>
</h:form>

</ui:composition>

And above Xhtml file is included in the below Xhtml file


<html lang="en"
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:c="http://java.sun.com/jsp/jstl/core"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:p="http://primefaces.org/ui"
    xmlns:my="http://java.sun.com/jsf/composite/mycomponents"
    xmlns:util="http://java.sun.com/jsf/composite/util">
   
    <ui:debug hotkey="x" />
    <h:head>
        <f:facet name="first">
            <meta http-equiv="X-UA-Compatible" content="EmulateIE8" />
            <meta content='text/html; charset=UTF-8' http-equiv="Content-Type"/>
        </f:facet>
       
        <link type="text/css" rel="stylesheet" href="#{request.contextPath}/css/default.css" />
        <link type="text/css" rel="stylesheet" href="#{request.contextPath}/css/syntaxhighlighter/syntaxhighlighter.css" />
<h:outputScript name="jsf.js" library="javax.faces"/>
<h:outputScript library="javascript" name="utils.js" target="head"/>
    </h:head>
    <h:body>
    <ui:include src="ui/test.xhtml"/>
</h:body>

</html>


Idea is very simple, so basically if I click the button it will make JS call to "submitHiddenForm" function, which will in turn call jsf.ajax.request (JSF standard) making call to JSF Bean.

The JS file would be placed under resources/javascript folder as JSF standard states.




function handleEvent(data){
    if(data.status === "complete"){
    var returnedTxt = data.responseText;
    document.getElementById("mainForm:outText22").value = returnedTxt;
    }
    }
   
    function submitHiddenForm(){
    document.getElementById("idHidden1").value = Math.random();
    jsf.ajax.request("idHidden1",
null,
{render: null, onevent: handleEvent, "a":112222, "b": 222});
    }

And the JSF Bean has the method implemented as:


public void valueChangeListener(ValueChangeEvent e) {
        System.out.println(" valueChangeListener() ");
        Map<String, String> reqParams = FacesContext.getCurrentInstance()
           .getExternalContext().getRequestParameterMap();
FacesContext context = FacesContext.getCurrentInstance();
System.out.println(" reqParams.get(a) "+reqParams.get("a"));
System.out.println(" reqParams.get(b) "+reqParams.get("b"));
try{
HttpServletResponse response = (HttpServletResponse)context.getExternalContext().getResponse();
ServletOutputStream out = response.getOutputStream();
out.println("SUCCESS");
out.flush();
}catch(Exception ex){
ex.printStackTrace();
}
context.responseComplete();
    }



In the above JSF bean method, I am using Servlet's outputstream to write the data I want as if it's any other Servlet or JSP output that could be used on the client side when the response is written.

NOTE: When I used servlet output stream to write the data, in that case on the client side in the "handleEvent" method, the "data" object has two statues only which are "begin" and "complete". But if I use  standard JSF facescontext to write message back then the statuses available are "begin", "complete" and "success"/"error" (did not try this, it could be failure also I guess).


FacesMessage message = new FacesMessage("Registration succesful!");
        FacesContext.getCurrentInstance().addMessage(null, message);

But even if I have two statuses when I use servlet output stream, I think I can handle the output accordingly.

So that's it, using above approach it gives some liberty to use Ajax communication with server side and without loosing much of JSF 2.0 benefits.

2 comments:

  1. Is something like below question possible?
    set-jsf-ui-component-value-from-jquery-instead-of-communicating-from-server

    ReplyDelete
    Replies
    1. you can have widgetVar="" associated with the element in question and then using that you can easily set/get it's value easily using jQuery.

      Delete