JSF and a cancel button

The Context
So in order to discuss JSF, there are three files (or groups of files) that need to be presented to get a complete view of the source. The files are presented at the end of this post, the important part is reposted here:
<h:inputText id="folderName" required="true"

value="#{folderHandler.current.name}" size="25"
disabled="#{folderHandler.submitDisabled}">
<mtl:validateFolderName length="3" />
</h:inputText>

<h:commandButton
value="#{labels.cancelButtonLabel}"
disabled="#{folderHandler.cancelDisabled}"
rendered="#{folderHandler.cancelRendered}"
action="#{folderHandler.cancel}" immediate="true">
</h:commandButton>

This presents a form with a single field for entry (folderName) with a special tag library mtl with a validateFolderName action. The general idea is that no folder should have a length less than three and it should not be named 'inbox' or 'trashbin' (since those are already exist). You also have buttons like Save.

The Problem
The lifecycle of the JSF page says that the validation will occur in this order (JavaWorld Article) . The validation occurs before any actionlistener gets notified. This makes it difficult to do the logic "If Cancel then ignore all validations".

The Solution
So there is a work around. Using the immediate="true" attribute of the commandButton short cuts around the validation (some people have written this up as a bug), in fact, it does not event push the data to the bean. This is perfect for a cancel button.

Presentation Layer (FolderEditForm.jsp)
<h:form>

<tr>
<td width="15%"><h:outputText value="Name" /></td>
<td>
<h:inputText id="folderName" required="true"
value="#{folderHandler.current.name}" size="25"
disabled="#{folderHandler.submitDisabled}">
<mtl:validateFolderName length="3" />
</h:inputText>
</td>
<td><h:message for="folderName" /></td>
</tr>
<tr>
<td colspan="3">
<hr/>
</td>
</tr>
<tr>
<td colspan="3">
Rules
</td>
</tr>
<tr>
<td width="100%" colspan="3">
<%@ include file="RulesGrid.jsp" %>
</td>
</tr>
<tr>
<td colspan="3" class="toolbar">
<h:commandButton
value="#{labels.submitButtonLabel}"
disabled="#{folderHandler.submitDisabled}"
rendered="#{folderHandler.submitRendered}"
action="#{folderHandler.submit}" />
<h:commandButton
value="#{labels.deleteButtonLabel}"
disabled="#{folderHandler.deleteDisabled}"
rendered="#{folderHandler.deleteRendered}"
action="#{folderHandler.delete}" />
<h:commandButton
value="#{labels.cancelButtonLabel}"
disabled="#{folderHandler.cancelDisabled}"
rendered="#{folderHandler.cancelRendered}"
action="#{folderHandler.cancel}" immediate="true">
</h:commandButton>
</td>
</tr>

</h:form>

Validation Component
/*

* Created on Jan 24, 2005 for Project maridspace
* Filename: FolderNameValidator.java
*
*/
package org.marid.application.faces.validator;
import java.io.Serializable;
import java.text.MessageFormat;
import java.util.Locale;
import java.util.ResourceBundle;

import javax.faces.application.Application;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.Validator;
import javax.faces.validator.ValidatorException;

/**
* @author pgirard
*/
public class FolderNameValidator implements Validator, Serializable {

/**
* Comment for serialVersionUID
*/
private static final long serialVersionUID = 1L;

private static final int MIN_LENGTH = 3;
private String _length;

/**
*
*/
public FolderNameValidator() {
super();
}

/**
*
* @param length
*/
public void setLength(String length) {
_length = length;
}

/**
* @param context
* @param component
* @param value
* @throws ValidatorException
*/
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {

Application application = context.getApplication();

String messageBundleName = application.getMessageBundle();
Locale locale = context.getViewRoot().getLocale();
ResourceBundle rb = ResourceBundle.getBundle(messageBundleName, locale);

if (_length == null) {
// if we havent received the length yet, try to get it
_length = (String) component.getAttributes( ).get("length");
}

Integer length = null;
try {
length = new Integer(_length);
} catch (NumberFormatException e) {
String msg = rb.getString("FolderNameValidator.invalid_length");
FacesMessage facesMsg =
new FacesMessage(FacesMessage.SEVERITY_FATAL, msg, msg);
throw new ValidatorException(facesMsg);
}
if (!(value instanceof String) || ((String) value).length() < length) {
String msg = rb.getString("FolderNameValidator.not_shorter");
String detailMsgPattern = rb.getString("FolderNameValidator.not_shorter_detail");
Object[] params = {MIN_LENGTH};
String detailMsg = MessageFormat.format(detailMsgPattern, params);
FacesMessage facesMsg = new FacesMessage(FacesMessage.SEVERITY_ERROR, msg, detailMsg);
throw new ValidatorException(facesMsg);
}
}
}


Business Component - not included here because it is quite huge.

Comments

Popular Posts