Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to stack messages with PrimeFaces messages component?

I have a question concerning the p:messages component.

First, here is my configuration:

  • PrimeFaces: 4.0.3 (elite)
  • JSF: MyFaces 2.0.2
  • Server: WebSphere 8.5.0.2

Then, my code:

test.xhtml

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
   xmlns:f="http://java.sun.com/jsf/core"
   xmlns:h="http://java.sun.com/jsf/html"
   xmlns:ui="http://java.sun.com/jsf/facelets"
   xmlns:p="http://primefaces.org/ui"
   xmlns:fn="http://java.sun.com/jsp/jstl/functions">

<h:head>
   <f:facet name="first">
       <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
       <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
   </f:facet>
   <meta http-equiv="cache-control" content="no-store,no-cache" />
   <meta http-equiv="pragma" content="no-cache" />
   <meta http-equiv="expires" content="0" />
</h:head>

<h:body>

   <div id="content">     
       <h:form id="form1"> 
           <p:tooltip/>
           <p:messages id="messages" showDetail="true" />
           <p:remoteCommand async="true" autoRun="true" process="@this" partialSubmit="true" action="#{testBean.checkA}" />
           <p:remoteCommand async="true" autoRun="true" process="@this" partialSubmit="true" action="#{testBean.checkB}" />
       </h:form>  
   </div>

</h:body>

</html>

Test.java

import java.io.Serializable;

import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.context.FacesContext;

import org.primefaces.context.RequestContext;

@ManagedBean(name="testBean")
@ViewScoped
public class Test implements Serializable {
    private static final long serialVersionUID = -1L;

    public void checkA() {
       try {
         Thread.sleep(2000);
       } catch (InterruptedException e) {
         e.printStackTrace();
       }
       FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO,"Message 1", null));
       RequestContext.getCurrentInstance().update("form1:messages");
    }  

    public void checkB() {
        FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_WARN,"Message 2", null));
        RequestContext.getCurrentInstance().update("form1:messages");
   } 
}

What this code does is really simple. When the page loads, two AJAX calls are made (checkA and checkB). I have put a Thread.sleep(2000) in checkA for testing purpose as I want it to finish after checkB. Once a method is finished, it sends back a message to the UI.

When I load the page, I see both AJAX calls made. CheckB will be finished first, so I will see "Message 2" on my page. But as soon as checkA is finished, the message is replaced by "Message 1". What I would like to do is append "Message 1" to "Message 2", so the user will see both messages on the screen. Is there any way to do it or is it a limitation of the component?

Secondly, as you can see in my code, I have to call RequestContext.getCurrentInstance().update("form1:messages"); in order to see my p:messages being updated. If I remove this line of code and add autoUpdate="true" to the component, it doesn't work...

One thing to take into consideration, unfortunately, I cannot change the configuration of the server as I don't manage it and there are already dozens of other apps on it.

Thanks in advance for your help!

like image 530
Nasedo47 Avatar asked Nov 08 '13 07:11

Nasedo47


2 Answers

The problem you've here is that FacesMessages are Request Scoped. As you're performing two ajax requests, when you reach the end of the second one the first message is not available at that moment.

You're using a @ViewScoped bean which remains alive till you leave the view itself, so the workaround should be having there a kind of stack/queue which stores the messages to display. When you consider the last request have been done, just add all your messages to the FacesContext and clear the stack.

Other choice would be to perform all your operations in the same method and display the messages you want after that. You would decrease network traffic because of performing one request instead of two, but your choice seems interesting if you want to render specific parts of the page before other ones.

About your updating issue, remember you can specify the part of the form you want to update directly in the page. Try adding update="messages" to your p:remoteCommand tags. About autoUpdate not working, possibly your remoteCommand's partialSubmit attribute could be carrying you on problems. Check out the definition of that attribute from Primefaces docs:

partialSubmit: Enables serialization of values belonging to the partially processed components only.

like image 90
Xtreme Biker Avatar answered Nov 18 '22 21:11

Xtreme Biker


You can use multiple p:message components:

<p:message id="message1"
    for="message1ValidatorCall" />
<h:inputHidden id="message1ValidatorCall" value="message1ValidatorCall">
    <f:validator validatorId="myValidator" />
</h:inputHidden>

<p:message id="message2"
    for="message2ValidatorCall" />
<h:inputHidden id="message2ValidatorCall" value="message2ValidatorCall">
    <f:validator validatorId="mySecondValidator" />
</h:inputHidden>

You should consider faces validators if you want to check or validate sth:

@FacesValidator("myValidator")
public class MyValidator implements Validator {
    @Override
    public void validate(FacesContext facesContext, UIComponent uiComponent, Object o) throws ValidatorException {
    //validator logic
    }
}
like image 41
Ömer Faruk Almalı Avatar answered Nov 18 '22 20:11

Ömer Faruk Almalı