Monday, August 22, 2011

GWT composite widgets vs. JSF composite components

I have started to learn GWT (Google Web Toolkit). I don't want to compare all features of both frameworks. I would like to pick just one feature. In GWT you can write autonomous and reusable composite widgets and apply them many times on one or several pages. GWT following say that's not really possible in JSF 2 because one composite component is normally linked with a managed bean. If you use the same composite component twice and change value(s) in one composite component, changed value(s) will be propogated through the bean to another component too. Is it really true?

Take an example. Assume, we have to write a composite widget / component consist of a label, an input field and a button which are aligned horizontally. The GWT solution could be looked like this one
public class CompositeExample implements EntryPoint
{
	private static class LabeledInput extends Composite {
		private Label label = new Label();
		private TextBox textBox = new TextBox();
		private Button button = new Button("Jump!", new ClickHandler() {
			public void onClick(ClickEvent event) {
				// do something
			}
		});

		public LabeledInput() {
			HorizontalPanel panel = new HorizontalPanel();
			panel.add(label);
			panel.add(textBox);
			panel.add(button);

			initWidget(panel);
		}
	}

	public void onModuleLoad() {
		LabeledInput labeledInput = new LabeledInput();
		RootPanel.get().add(labeledInput);
	}
}
I defined a class named LabeledInput which extends the class com.google.gwt.user.client.ui.Composite. The new composite widget LabeledInput is well reusable many times on any pages. No problems here. A corresponding JSF solution could be implemented in a file labeledInput.xhtml as follows
<html xmlns="http://www.w3.org/1999/xhtml"
	  xmlns:h="http://java.sun.com/jsf/html"
	  xmlns:cc="http://java.sun.com/jsf/composite">
<cc:interface>
	<cc:attribute name="model" required="true">
		<cc:attribute name="labelValue" required="true"/>
		<cc:attribute name="inputValue" required="true"/>
		<cc:attribute name="jump" required="true" method-signature="void f(javax.faces.event.ActionEvent)"/>
	</cc:attribute>
	<cc:actionSource name="jump"/>
</cc:interface>
<cc:implementation>
	<h:panelGrid columns="3">
		<h:outputLabel for="#{cc.clientId}:input" value="#{cc.attrs.model.labelValue}"/>
		<h:inputText id="input" value="#{cc.attrs.model.inputValue}"/>
		<h:commandButton value="Jump!" actionListener="#{cc.attrs.model.jump}"/>
	</h:panelGrid>
</cc:implementation>
</html>
cc:attribute "model" should point to a managed bean having attributes "labelValue", "inputValue" and method (actionListener) "jump". Using of this custom component is simple
<h:form>
	...
	<custom:labeledInput id="cli1" model="#{someBean}">
	...
</h:form>

<h:form>
	...
	<custom:labeledInput id="cli2" model="#{someBean}">
	...
</h:form>
There is a problem with this JSF solution. If you change the input value (field h:inputText) in the first component and submit the surrounding form, the new value is going to set into the bean "someBean". This bean is also used by the second component. That means, the second component gets the changed value what is not a desired behavior of course. But JSF is powerful enough to solve this problem. There is a possibility to mix JSF components written in Java with declarative code written in XHTML. We can write a class LabeledInput extending UINamingContainer (according to specification) for our composite component. This class encapsulates then all attributes and has the mentioned above actionListener method.
@FacesComponent("mypackage.LabeledInput")
public class LabeledInput extends UINamingContainer
{
	enum PropertyKeys {labelValue, inputValue, jump;}

    public Object getLabelValue() {
		return getStateHelper().eval(PropertyKeys.labelValue);
    }

    public void setLabelValue(Object labelValue) {
		getStateHelper().put(PropertyKeys.labelValue, labelValue);
    }

    public Object getInputValue() {
		return getStateHelper().eval(PropertyKeys.inputValue);
    }

    public void setInputValue(Object inputValue) {
		getStateHelper().put(PropertyKeys.inputValue, inputValue);
    }

	public void jump(ActionEvent e) {
		// do something
	}
}
Component class can be linked now with the XHTML part via "componentType" attribute in cc:interface. Important part to be changed looks then as follows
<cc:interface componentType="mypackage.LabeledInput">
	<cc:attribute name="labelValue"/>
	<cc:attribute name="inputValue"/>
	<cc:actionSource name="jump"/>
</cc:interface>
<cc:implementation>
	<h:panelGrid columns="3">
		<h:outputLabel for="#{cc.clientId}:input" value="#{cc.labelValue}"/>
		<h:inputText id="input" value="#{cc.inputValue}"/>
		<h:commandButton value="Jump!" actionListener="#{cc.jump}"/>
	</h:panelGrid>
</cc:implementation>
Managed bean is not needed to be passed by this way. Now we can speak about an autonomous and reusable composite component and write <custom:labeledInput> on a page as much as we want without any collisions.

1 comment:

  1. After finish reading, I am right choosed GWT instead of JSF. I can learn and make application quickly using GWT . Before that I try to learn JSF but get frustration and feel it's take much time to learn and make it in use. I am satisfied finding GWT just make use of OOP concept and make widget reuse in action.

    ReplyDelete

Note: Only a member of this blog may post a comment.