4

I would like to create a custom message renderer to renders h:message as a 'p' html element instead of as a 'span' element. It concerns the following message tag:

<h:message id="firstNameErrorMsg" for="firstname" class="error-msg"  />

I've written to code underneath, but that's only rendering an empty 'p' element. I suppose I have to copy all attributes and text from the original component and write it to the writer. However, I don't know where to find everything and it seems to be a lot of work for just a replacement of a tag.

Is there a better way to get an h:message tag rendered as a 'p' element?

Code:

@FacesRenderer(componentFamily = "javax.faces.Message", rendererType = "javax.faces.Message")
public class FoutmeldingRenderer extends Renderer {

    @Override
    public void encodeEnd(final FacesContext context, final UIComponent component) throws IOException {

        ResponseWriter writer = context.getResponseWriter();
        writer.startElement("p", component);
        writer.endElement("p");

    }
}
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
Kees de Boer
  • 41
  • 1
  • 2

1 Answers1

7

It isn't exactly "a lot of work". It's basically a matter of extending from the standard JSF messages renderer, copypasting its encodeEnd() method consisting about 200 lines then editing only 2 lines to replace "span" by "p". It's doable in less than a minute.

But yes, I agree that this is a plain ugly approach.

You can consider the following alternatives which are not necessarily more easy, but at least more clean:

  1. First of all, what's the semantic value of using a <p> instead of a <span> in this specific case? To be honest, I'm not seeing any semantic value for this. So, I suggest to just keep it a <span>. If the sole functional requirement is to let it appear like a <p>, then just throw in some CSS. E.g.

    .error-msg {
        display: block;
        margin: 1em 0;
    }
    

  2. You can obtain all messages for a particular client ID directly in EL as follows, assuming that the parent form has the ID formId:

    #{facesContext.getMessageList('formId:firstName')}
    

    So, to print the summary and detail of the first message, just do:

    <c:set var="message" value="#{facesContext.getMessageList('formId:firstName')[0]}" />
    <p title="#{message.detail}">#{message.summary}</p>
    

    You can always hide it away into a custom tag file like so:

    <my:message id="firstNameErrorMsg" for="firstname" class="error-msg" />
    

  3. Use OmniFaces <o:messages>. When the var attribute is specified, then you can use it like an <ui:repeat>.

    <o:messages for="firstNameErrorMsg" var="message">
        <p title="#{message.detail}">#{message.summary}</p>
    </o:messages>
    
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • Hi BalusC, Thanks for the possibilities for a solution to my problem you've offered. I've used the first one you handed, mainly because it was the fastest and, indeed, easiest to implement. The gigantic sizes of the various stylesheets I'm forced to work with and reluctance of the owners to let my change them also kept me from going for the css option. Furthermore I'm already using some other jsf tag library than OmniFaces, so can't really use the OmniFaces option either. Thanks again and regards, Kees – Kees de Boer Nov 04 '13 at 22:04
  • OmniFaces is not a component library, but an utility library and in essence compatible with every component library. You can use it in combination with PrimeFaces, RichFaces, ICEFaces, etc. – BalusC Nov 05 '13 at 00:25