Label Provider for JSF Input Components

When creating forms with JSF, one thing I don’t enjoy much is having to define label for an input twice, one for h:outputLabel and one for input component itself to provide better looking validation messages. If you don’t provide label option on an input component, JSF uses client id so you end up with messages like “_j12:input : Value is required”. Solution is to use label like following to get “Age: Value is required”


<h:outputLabel for="age" value="Age" />

<h:inputText id="age" value="#{bean.property}" label="Age" required="true"/>

Problem is clear right, we have to define “Age” twice. Wouldn’t it be cool if an input component’s label value is retrieved from the corresponding outputLabel? After thinking about this some time, I’ve decided to use the hidden gem of JSF 2.0, System Events. Here we go;

package org.primefaces.rocks.event;

import javax.faces.component.UIComponent;
import javax.faces.component.html.HtmlOutputLabel;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.ListenerFor;
import javax.faces.event.SystemEvent;
import javax.faces.event.SystemEventListener;

@ListenerFor(sourceClass=HtmlOutputLabel, systemEventClass=javax.faces.event.PreValidateEvent)
public class LabelProvider implements SystemEventListener {

    public boolean isListenerForSource(Object source) {
        return true;
    }

    public void processEvent(SystemEvent event) throws AbortProcessingException {
        HtmlOutputLabel outputLabel = (HtmlOutputLabel) event.getSource();
        UIComponent target = outputLabel.findComponent(outputLabel.getFor());

        if(target != null) {
            target.getAttributes().put("label", outputLabel.getValue());
        }
    }
}

What it does is simple, it runs on any HtmlOutputLabel component on page just before ProcessValidations phase and sets the label attribute of input with value of outputLabel. So now we can do the following and still get nice messages like “Age : Value is required”.


<h:outputLabel for="age" value="Age" />

<h:inputText id="age" value="#{bean.property}" required="true"/>

Listener can be improved for performance as we don’t want to component search on every request for this, changing event to PreRender and isPostback check will do the job. My PrimeFaces team mate Yigit Darcin suggested adding this to PrimeFaces. We’ll discuss that further.

11 Responses to Label Provider for JSF Input Components

  1. siva says:

    Really cool!

  2. chenshu says:

    @ListenerFor(sourceClass=javax.faces.component.html.HtmlOutputLabel, systemEventClass=javax.faces.event.PreValidateEvent)
    The above line can’t be compiled,always reports
    can’t find html

  3. chenshu says:

    I changed it,it can be compiled now.
    @ListenerFor(sourceClass=HtmlOutputLabel.class,systemEventClass=PreValidateEvent.class)

  4. maxtorzito says:

    Great

  5. chenshu says:

    processEvent method was not called.😦

  6. Cagatay,
    This is a great tip. Now that you are releasing form components – would you considering merging the label and inputfield into one Primefaces component with a attribute exposed for label? That way developers can just use one component and not have to worry about this. We can still continue to use the panelGrid and columnclasses etc.

  7. addition to my prev comment. If you see Apache Trinidad, they have done something similar

  8. bitec says:

    Not working for me. The method processEvent is not called

  9. Try defining your listener in faces-config then instead of annotation.

  10. chenshu says:

    com.webt.listener.LabelProvider
    javax.faces.component.html.HtmlOutputLabel
    javax.faces.event.PreValidateEvent

    It works.Could we think there is a bug with annotation @ListenFor here?

  11. Carlos Natalino says:

    Thanks Catagay,

    To it works in my projects, i have to add this code in faces-config.xml:

    utils.view.LabelProvider
    javax.faces.event.PreValidateEvent

    And modify this method:
    @Override
    public boolean isListenerForSource(Object source) {
    if (source instanceof HtmlOutputLabel)
    return true;
    else
    return false;
    }

    I think a good increase of this feature is the follow:
    if (target != null && target.getAttributes().get(“label”) == null)
    target.getAttributes().put(“label”, sLabel);

%d bloggers like this: