In the first part of the article, we showed how to let a JSF bean drive a GWT module through an exposed JavaScript API. Now we’ll see how to make a GWT module raise events in a way such that a call to the server is made letting it respond accordingly.
Defining the events to be raised
We’ll start by defining which events will be raised by our GWT module. Let’s put them all together in a single interface, which as a result of hours of thinking and thinking I decided to name IHelloWorldEventHandlers.
public interface IHelloWorldEventHandlers {
void requestHelloWorldAlert(String name);
}
Well, there will be only one event. Suppose we have a textbox containing the user name and a button which when clicked will raise the event.
Remember that one of our goals is to let JSF handle the request to the server, so the event will be implemented as a call to a JavaScript function which we’ll suppose to have already been defined somewhere. The concrete class which we’ll be called in the button’s onClick looks like the one below:
public class HelloWorldEventHandlers implements IHelloWorldEventHandlers {
public native void requestHelloWorldAlert(String name) /*-{
$wnd.requestHelloWorldAlert(name);
}-*/;
}
Some things to point out about this. You’ve probably have noticed that the method is defined with the “native” modifier. Think of this just as a way of wrapping a piece of JavaScript code with a Java method definition.
The second thing to point out is the $wnd reference. This is just a name for the global JavaScript scope in the browser.
The last, but the most important is that we are following a convention in the way we write the method body. The method in JavaScript is called exactly the same as the Java event handler, and it also has the same arity. This way we’ll be able to do some useful stuff by reflection later.
Dynamically Generating JavaScript handlers for the events
Now we have to create the JavaScript functions which will be called. The first idea that comes to mind is to write a <a4j:jsFunction> for each event. Well, that would work, but we can do better than that. There’s a common pattern between all the functions that we’ll need to write: we’ll want an action to be executed on a backing bean and we’ll have to create an <a4j:actionParam> for each parameter in the function.
We can then use reflection and the RichFaces and JSF object models to dynamically generate the functions for us. We decided to implement this through a new custom component. Again, this article does not intend to show how to write custom JSF components, but we’ll describe the basic concepts involved in the solution.
Our component we’ll need three attributes: the name of a backing bean, the name of a Map object property of the same bean and the name of an element to reRender.
We’ll override the setProperties method of our custom component CustomComponentTag class to add the dynamic generation of the jsFunction components through the following code:
FacesContext currentFacesCtx = FacesContext.getCurrentInstance();
Application application = currentFacesCtx.getApplication();
String reRenderedComponentId = (String) component.getAttributes()
.get("reRenderedComponent");
String beanId = (String) component.getAttributes().get(
"bean");
String functionParamsMapId = (String) component.getAttributes().get(
"functionParams");
for (Method method : IHelloWorldEventHandlers.class.getMethods()) {
HtmlAjaxFunction gwtEventHandler = new HtmlAjaxFunction();
//Set the name of the a4jFunction to be the same as the method
//name in IHelloWorldEventHandlers
gwtEventHandler.setName(method.getName());
gwtEventHandler.setReRender(reRenderedComponentId);
gwtEventHandler.setAction(application.createMethodBinding("#{"
+ beanId + "." + method.getName() + "}", null));
int i = 1;
for (Class> paramType : method.getParameterTypes()) {
String paramName = "Param" + i;
HtmlActionParameter htmlActionParam = new HtmlActionParameter();
htmlActionParam.setName(paramName);
htmlActionParam.setAssignToBinding(createValueBinding("#{"
+ beanId + "." + functionParamsMapName
+ "['" + method.getName() + "." + paramName + "']}"));
gwtEventHandler.getChildren().add(htmlActionParam);
htmlActionParam.setParent(gwtEventHandler);
//Important: we have to add this line in order to
//pass the parameter.
//It is not enough to add the actionParam as
//a child of the a4j:jsFunction.
gwtEventHandler.addActionListener(htmlActionParam);
i++;
}
component.getChildren().add(gwtEventHandler);
}
Note that we only need to know the IHelloWorldEventHandlers to generate the a4jFunction components. If we refactor the handlers changing their arity or name, this stuff will go on working. The only caveat is that we are forced to write those weird native methods in the implementation of IHelloWorldEventhandlers, but we think it is possible to work around that problem using GWT Generators also.
Another thing to point out is that we are using the Map to pass parameters, where the method name concatenated with a generated param name is used as key.
Martín
Good post!!
I’m looking into integrating both also, so this should help out. I was thinking of doing something with the a4j:jsFunction also.
Quick question to you. When you have a panel GWT component, how do you put a piece of a JSF page inside of it? Since one is client side, and the JSF needs to be processed by the server. I haven’t look at the details of the HTML Panel in GWT, so I don’t know if you can specify the component ID of the piece of the page you want to add. IF you can then it’s a no brainer. Have you done that?
Hi, Jose! In fact, what we did was to embed a GWT component into a JSF page, the interesting part of this is that the JSF controller drives the GWT component and at the same time the GWT component raises events that are sent to the server.
What you need to do (if I got you well), is the other way round.
Leaving that apart, I couldn’t imagine a scenario where I would want to put a piece of page-oriented view inside a component-oriented one.
I’m willing to help (and interested in such a scenario), so don’t hesitate to drop me an email at mverzilli@manas.com.ar for further discussion on the topic.
Jose and Martin
If you have time post about that , its an interesting discussion. I’m just begging in programing , so it would be nice to learn about such interesting subject
Felipe, what are you specifically interested in? If you give me some further details of your interests and it’s something not out of reach for me, I can prepare a post. Also, if you have any special doubt you’re question is very welcome.
Regards,
Martín
Martin , wonderful GWT – JSF integration article…
Do you know why G4JSF is dead?
I think would be a very interesting tool for developers and for faces community
Hi,
It’s a perfect article and very interesting. As I understood its a way to integrate GWT-Components into JSF/Richfaces environment which simplifies the development of own components. Can you provide the whole sample code as a download to have all the glue together? This would be really helpful.
Thx,
juergen
Hi, Juergen! Thanks for your comment! In fact I’ve factored the post out of production code I had to develop for a project, and as you may have noticed it’s been a while since I’ve done it. It would be great if you could specify which parts of the solution you find more obscure, so I can write a little sample focusing on that, or directly help you do it by yourself. You can also write me to mverzilli@manas.com.ar.
Cheers