Monday, October 5, 2009

Extend JSF Command Link

I found myself adding a Remove link throughout my JSF application. It looked like this:

<h:commandlink value="Remove" actionlistener="#{MyBean.remove}">


I wanted to add a confirm dialog. So I added this.

<h:commandlink value="Remove" actionlistener="#{MyBean.remove}" onclick="javascript return confirm('Are you sure')">


But as I copied and pasted this through 5 or 6 different pages, I got to thinking, there has to be a better way.

So I created a custom jsf component by extending Command Link.

Step 1

First step was to create my new tag. It simply wraps the default command link.

package com.acme;

import com.sun.faces.taglib.html_basic.CommandLinkTag;
import javax.el.MethodExpression;
import javax.el.ValueExpression;
import javax.faces.component.UIComponent;
import javax.faces.component.html.HtmlCommandLink;
import javax.faces.event.MethodExpressionActionListener;
import javax.faces.webapp.UIComponentELTag;

/**
* @author cmcintosh
*/
public class RemoveCommandLinkTag extends UIComponentELTag { // Declare a bean property for the hellomsg attribute.

CommandLinkTag commandLink = new CommandLinkTag();
ValueExpression value;
MethodExpression actionListener;

// Associate the renderer and component type.
public String getComponentType() {
return commandLink.getComponentType();
}

public String getRendererType() {
return commandLink.getRendererType();
}

@Override
protected void setProperties(UIComponent component) {
super.setProperties(component);
component.setValueExpression("value", value);
((HtmlCommandLink)component).addActionListener(new MethodExpressionActionListener(actionListener));
((HtmlCommandLink)component).setOnclick("javascript:return confirm('Are you sure you want to remove?')");

}
@Override
public void release() {
super.release();
commandLink.release();

}

public void setValue(ValueExpression value) {
this.value = value;

}
public void setActionListener(MethodExpression actionListener) {
this.actionListener = actionListener;
}
}

Step 2
Next I needed to create a tld.


<taglib version="2.0" xmlns="http://java.sun.com/xml/ns/j2ee" xsi="http://www.w3.org/2001/XMLSchema-instance" schemalocation="http://java.sun.com/xml/ns/j2ee web-jsptaglibrary_2_0.xsd">
<tlib-version>1.0</tlib-version>
<short-name>datastore</short-name>
<uri>/WEB-INF/tlds/datastore</uri>
<tag>
<name>RemoveCommandLink</name>
<tag-class>com.acme.datastore.web.components.tags.RemoveCommandLinkTag</tag-class>
<attribute>
<name>value</name>
<deferred-value>
<type>java.lang.String</type>
</deferred-value>
</attribute>
<attribute>
<name>actionListener</name>
<required>false</required>
<deferred-method>
<method-signature>
actionListener(javax.faces.event.ActionEvent)
</method-signature>
</deferred-method>
<type>String</type>
</attribute>

</tag>
</taglib>


This was one of the hardest parts note the deferred-method and deferredvalue. I've never encountered those before.

Step 3

Add it to the web.xml:
<jsp-config>
<taglib>
<taglib-uri>http://datastore.acme.com/datastore</taglib-uri>
<taglib-location>/WEB-INF/tlds/datastore.tld</taglib-location>
</taglib>
</jsp-config>

Step 4
Finally to use it:
<%@taglib prefix="ds" uri="http://datastore.acme.com/datastore"%>
.
.
.
<ds:removecommandlink value="Remove" actionlistener="#{ViewModifyTable.removeColumn}">
<f:param name="applicationId" value="#{ViewModifyTable.applicationId}">
<f:param name="tableName" value="#{ViewModifyTable.tableName}">
</f:param></f:param></ds:removecommandlink></h:commandlink></h:commandlink>