Thursday 4 November 2010

Required field indication with ExtVal - Part 1

Introduction
By using the MyFaces extension Validation framework, ExtVal called in short, we are able to create powerful validation rules that can be used in JSF. Validation rules are no longer encoded in the view layer, the JSP or the XHTML files, but defined by placing annotations on the model and backing bean classes. And that is also where they belong, since validation is linked to the data and not the representation we happen to have or like.

But with ExtVal, we can no longer use the custom renderers we have to indicate that a field is required. Without ExtVal, is was common to have a custom renderer that looked at the required property of the field and allowed us to have some indication that the field is required. That could be a style class that changes the appearance (a yellow background on the field) or some character like an asterisk by the label that goes with the field. Such custom renderers no longer work by default, since the
components in the tree have no longer indications of the validations that are needed.

setting up ExtVal
You can activate, within ExtVal, the option that the Input fields are 'populated' with the correct required property value, so that the custom renderers that operate on the field itself, start working again.
But during the rendering of the label in front of the field, this property isn't available yet.

DefaultExtValCoreConfiguration.overruleActivateRequiredInitialization(true, true);

or for version before x.x.4 it was

ExtValContext.getContext().addGlobalProperty("mode:init:required", Boolean.TRUE, true);
But there is a better way of doing this, natively supported by ExtVal. And also for the label, there is a solution.

First, I have to make a remark when we use MyFaces Trinidad. The component library has built-in support for an indication on the label when the field it is attached to, is required. By adding the ExtVal Trinidad component support to your project, the option explained above, is activated and thus the required label indication for Trinidad is still available. We still can use and see required fields.

For those that don't use Trinidad, there is a small configuration needed or an additional add-on to have the functionality. In this text, I'll explain the possibilities to indicate the required nature of the field on the Input field itself. The next time, I'll explain the Required Label add-on functionality so that you can have an indication on the label that goes with the field.

Into ExtVal
ExtVal has the concept of the ComponentInitializer. And as we go to the class or JavaDoc, we see it has one method, configureComponent, which will be called during rendering to place some meta-data of the validation on the JSF components. By default, the length information (expressed by validation rules like
@Length or @Column(size=) ) are set by such component initializers.

We can create or own component initializers that, for instance add a style class to the component, when the component is required. The configureComponent method has a parameter metadata, which is a list of some metadata that is extracted from the validation annotations. The keys of the map are the constants defined by the CommonMetaDataKeys class.

This Map is obtained by a call to the method ExtValUtils.getTransformedMetaDataFor that calls another concept within ExtVal, MetaDataTransformer, that defines the information based on the information on the annotation, like length or that the
annotation indicates that it is required. I just mention this, if you ever have the need to create your own transformer.

Change background color of required fields
Enough technical information. What do we need to do so that all required fields have a yellow background.

  1. Activate the required initialization option so that the JSF component required attribute gets set.
  2. Write a custom ComponentInitializer that adds a style class to the component when it is required.


By activating the required initialization, the default component initializers in the property validation module and the bean validation module, will set the required attribute of the JSF component. The metaData parameter of the configureComponent method will always contain the information if the field should be required or not. But using the default component initializers has some advantages. Within ExtVal there are things possible like partial and conditional validations (like field is only required on a certain screen and validation groups of the Bean Validation specs). So it is much easier that we let ExtVal decide if the JSF component is really required and then, have an additional ComponentInitializer, that doesn't look anymore to the metadata, but inspects the JSF component required attribute.

The ComponentInitializers can be ordered, so that we can guarantee that the JSF component required attribute has the correct value. This is the code we have to define then

public class CustomStartupListener extends AbstractStartupListener
{

    private static final long serialVersionUID = -6978602999848589643L;

    @Override
    protected void init()
    {
        DefaultExtValCoreConfiguration.overruleActivateRequiredInitialization(true, true);
        ExtValContext.getContext().addComponentInitializer(new RequiredFieldComponentInitializer());
    }
}
This startup listener must be defined as JSF phaseListener in the Faces configuration file.

@InvocationOrder(value = 1000)
 public class RequiredLabelComponentInitializer implements ComponentInitializer
{

    private Logger logger = LoggerFactory.getLogger(getClass());


    public void configureComponent(FacesContext facesContext, UIComponent uiComponent, Map<
                                   String, Object> metaData)
    {
        if (uiComponent instanceof UIInput)
        {
            UIInput input = (UIInput) uiComponent;
            if (input.isRequired())
            {
                changeStyleClass(uiComponent);
            }
        }
    }

    private void changeStyleClass(final UIComponent uiComponent)
    {
        Method method = ReflectionUtils.tryToGetMethod(uiComponent.getClass(), "getStyleClass");
        if (method != null)
        {
            String styleClass = null;
            try
            {
                styleClass = (String) ReflectionUtils.invokeMethod(uiComponent, method);
                styleClass = StringUtils.insertAdditionalStyleClass(styleClass, "requiredFlag");

                // Should always succeed.
                method = ReflectionUtils.getMethod(uiComponent.getClass(), "setStyleClass", String.class);

                ReflectionUtils.invokeMethod(uiComponent, method, styleClass);
            }
            catch (InvocationTargetException e)
            {
                logger.error("This should not happen but you see, it can", e);
            }
            catch (IllegalAccessException e)
            {
                logger.error("This should not happen but you see, it can", e);
            }
            catch (NoSuchMethodException e)
            {
                logger.error("This should not happen but you see, it can", e);
            }

        }
    }
}

Conclusion
By just defining a ComponentInitializer and making the proper configuration of it within ExtVal, we can have any type of input field to change his background color when it is required. This in contrast to the custom renderer solution where you need a specific custom renderer for each type of input (InputField, InputArea, ...) and library (RichFaces, PrimeFaces, ...).

The next time, we discuss about the required label add-on to have some indication placed on the label that goes with the field.

The example can be downloaded here .

No comments:

Post a Comment