How to number JSF error messages and attach number

2020-07-13 09:11发布

问题:

I want to number any JSF error messages (e.g. validation, conversion messages) that appears if a user enters invalid input in a couple of input fields. For example like this when the user sends the form:


  1. Name cannot contain numbers
  2. E-mail is incorrect

----- Form ------

(1) Name: John 13 Doe

Age: 30

(2) E-mail: myemail@domain


How can I do this?

Best regards

回答1:

For the messages part, just display them all using <h:messages/> the usual way which you style as list-style-type: decimal;. That would display the numbers instead of bullets.

Next step would be numbering the labels. This requires a bit more work as JSF doesn't provide any builtin facility to figure that. Basically you'll need to determine whether the label in question has a message associated or not and in which order the message has been enqueued. You could collect this information into a Map<String, String> as follows:

private Map<String, String> messageIndexes;

public Map<String, String> getMessageIndexes() {
    FacesContext context = FacesContext.getCurrentInstance();
    if (messageIndexes == null && context.getRenderResponse()) {
         messageIndexes = new HashMap<String, String>();
         Iterator<String> clientIds = context.getClientIdsWithMessages();
         for (int i = 1; clientIds.hasNext(); i++) {
             messageIndexes.put(clientIds.next(), String.format("(%d)", i));
         }
    }
    return messageIndexes;
}

and then use it as follows:

<h:form id="form">
    <h:outputText value="#{bean.messageIndexes['form:input1']}" />
    <h:outputLabel for="input1">Label 1</h:outputLabel>
    <h:inputText id="input1" value="#{bean.input1}" required="true" />
    <br />
    <h:outputText value="#{bean.messageIndexes['form:input2']}" />
    <h:outputLabel for="input2">Label 2</h:outputLabel>
    <h:inputText id="input2" value="#{bean.input2}" required="true" />
    <br />
    ...
</h:form>

If the client ID is not present in the map, then simply nothing will be rendered.

To move all the job out of the bean, you may consider either a PhaseListener (do it in the beforePhase() of the RENDER_RESPONSE), or a custom label component.



回答2:

Thank you very much. I used your solution and did some minor changes. I solved it by using a PhaseListener for RENDER_RESPONSE phase. However, I did have to call method below from beforePhase() instead of afterPhase(). Otherwise my changes had no effect.

public void addMessageIdentifier() {
    FacesContext context = FacesContext.getCurrentInstance();
    if (context.getRenderResponse()) {

        // Iterate over faces messages and add counter
        Iterator<FacesMessage> messages = context.getMessages();
        if (messages != null && messages.hasNext()) {
            int counter = 1;
            while (messages.hasNext()) {
                FacesMessage message = messages.next();
                if (message.getSeverity().equals(FacesMessage.SEVERITY_ERROR)) {
                    String indicator = "(" + new Integer(counter).toString() + ") ";
                    message.setDetail(indicator);
                    message.setSummary(indicator + message.getSummary());
                    counter++;
                }
            }
        }
    }
}