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:
- Name cannot contain numbers
- E-mail is incorrect
----- Form ------
(1) Name: John 13 Doe
Age: 30
(2) E-mail: myemail@domain
How can I do this?
Best regards
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.
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++;
}
}
}
}
}