Search code examples
jsf-2uicomponentsicefaces-2viewroot

JSF2 + IceFaces 2 - Retrieve UIComponent from ViewRoot


I've got hard time resolving the following. My problem is quite simple : I would like to highlight in red the forms fields that triggered validation errors. The error messages are placed correctly in the FacesContext using a context.addMessage(...) line.

I'd like my system to be generic. All form fields having a message attached are automatically highlighted.

I've found on this site a link to this excellent article : http://www.jroller.com/mert/entry/how_to_find_a_uicomponent

With it, I did implement a PhaseListener for the RENDER_RESPONSE phase, which do the following :

@Override
  public void beforePhase(PhaseEvent event) {
    // get context
    FacesContext context = event.getFacesContext();

    // iterate on all the clientIds which have messages
    Iterator<String> clientIdsWithMessages = context.getClientIdsWithMessages();
    while (clientIdsWithMessages.hasNext()) {

      // get the clientId for the field component
      String clientIdWithMessage = clientIdsWithMessages.next();
      // split on ":"
      String[] splitted = clientIdWithMessage.split(":");

      UIComponent component = findComponentInRoot(splitted[splitted.length - 1]);
      if (component != null) {
        Map<String, Object> attributes = component.getAttributes();

        if (attributes.containsKey("style")) {
          attributes.remove("style");
        }
        attributes.put("style", "background-color: #FFE1E1;");
      }
    }
  }

This perform perfectly well for almost all my usage.

Now, where it becomes a bit tricky, is that some of my forms have such code :

<ice:dataTable id="revisionDocuments" value="#{agendaBean.agenda.revisionsDocuments}" var="revision">
    <ice:column>
        <ice:inputText value="#{revision.sequenceAdresse}" id="revisionSequenceAdresse" />
    </ice:column>
    ....

The generated form has several lines (one for each object of the revisionsDocuments list), and each element has a unique identifier (clientId) which looks like :

contentForm:revisionDocuments:0:revisionSequenceAdresse

With 0 changed for 1, 2, ... for each iteration. Consequently, the code provided to search the UIComponent from ViewRoot does not work properly. All forms fields have the same "id". What surprise me more is : they have the same "clientId" in FacesContext too :

contentForm:revisionDocuments:revisionSequenceAdresse

I cannot distinguish, while going through the tree, if I do see the right form field or any of the others.

Does anyone have a hint to solve this ? Or another suggestion to implement the highlight of my fields ? I have to admit, I dont really like my code, I consider dirty to manipulate the viewRoot like I'm doing, but I could not figure out a better solution to have a generic highlight of my fields.

I'm running IceFaces 2.0.2 with JSF-Impl 2.1.1-b04 on JBOss AS 7.0.2.Final.

Thank you in advance for the answers. Best regards, Patrick


Solution

  • You should apply this in the client side instead. You've got a collection of client IDs with messages. One of the ways is to pass this information to JavaScript and let it do the job. You can find an example of such a PhaseListener in this article: Set focus and highlight in JSF.

    Since JSF 2.0 there is however another way without the need for a PhaseListener. There's a new implicit EL variable, #{component} which refers to the UIComponent instance of the current component. In case of UIInput components, there's an isValid() method. This allows you to do something like:

    <h:inputText styleClass="#{component.valid ? '' : 'error'}" />
    

    with this in a CSS file:

    .error {
        background: #ffe1e1;
    }
    

    (yes, you can also do this in a style attribute, but mingling style with markup is a poor practice)

    To abstract this away (so that you don't need to repeat it in every input), you can just create a composite component for this, something like <my:input>.