JSF has some great form validation features, but what if you want to add some style for the erroneous form fields? There are no standard ways to accomplish that.
Luckily for us, we can do just that with the help of PhaseListener, using the RENDER_RESPONSE phase id. What we need to do is:
- get client ids with messages iterator from the FacesContext
- iterate ids and find components from UIViewRoot
- set your error style to found components
- make some kind of mechanism to save/restore original style of components
Here’s simple implementation of ValidationStylePhaseListener:
package electro.faces.listeners;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.faces.component.UIComponent;
import javax.faces.component.UIInput;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
public class ValidationStylePhaseListener implements PhaseListener {
private final String ORIGINAL_STYLE = "electro.faces.original.style";
public PhaseId getPhaseId() {
return PhaseId.RENDER_RESPONSE;
}
public void beforePhase(PhaseEvent phaseEvent) {
FacesContext context = FacesContext.getCurrentInstance();
UIViewRoot root = context.getViewRoot();
restoreOriginalStyles(context, root);
Iterator i = context.getClientIdsWithMessages();
while (i.hasNext()) {
String id = i.next();
UIComponent component = root.findComponent(id);
if (component instanceof UIInput) {
String style = (String) component.getAttributes().get("styleClass");
style = style == null ? "" : " " + style;
component.getAttributes().put("styleClass", "error" + style);
saveOriginalStyle(id, style, context);
}
}
}
private void restoreOriginalStyles(FacesContext context, UIViewRoot root) {
Map session = context.getExternalContext().getSessionMap();
if (session.containsKey(ORIGINAL_STYLE)) {
List list = (List) session.get(ORIGINAL_STYLE);
for (Map item : list) {
Map.Entry entry = item.entrySet().iterator().next();
UIComponent component = root.findComponent(entry.getKey());
if (component != null) {
component.getAttributes().put("styleClass", entry.getValue());
}
}
session.remove(ORIGINAL_STYLE);
}
}
private void saveOriginalStyle(String id, String style, FacesContext context) {
Map session = context.getExternalContext().getSessionMap();
Map originalStyle = new HashMap();
originalStyle.put(id, style);
if (session.get(ORIGINAL_STYLE) == null) {
session.put(ORIGINAL_STYLE, new ArrayList());
}
((List)session.get(ORIGINAL_STYLE)).add(originalStyle);
}
public void afterPhase(PhaseEvent phaseEvent) {}
}
The method saveOriginalStyle, saves original components style to the session.
The method restoreOriginalStyles resets all style changes, before checking for new erroneous fields.
Now we can register our PhaseListener in faces-config.xml:
<lifecycle>
<phase-listener>electro.faces.listeners.ValidationStylePhaseListener</phase-listener>
</lifecycle>
After that, erroneous form elements should have an “error” style class added to their HTML class attribute.
this helped me so much!! Excellent job, thank you!
The dang code doesn’t even COMPILE.
Oh yeah observer, it shouldn’t if you compile it with C :)