10

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!

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
Nasedo47
  • 337
  • 3
  • 13

2 Answers2

4

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.

Community
  • 1
  • 1
Aritz
  • 30,971
  • 16
  • 136
  • 217
  • In my real page, I make 3 AJAX calls as I need to call 3 Web Services and I don't want the user to wait for them to finish. Those 3 WS simply return a status (icon), so I update each of those 3 icons on the page as soon as I get a result for one WS. That's why I prefer to keep all my AJAX calls instead of only one. Your idea of making a queue is interesting. – Nasedo47 Nov 08 '13 at 08:34
  • Keeping a queue in the bean allows you to choice what you want to display in each moment. You can display them all with the last request or just display each of them when each of the requests finishes, controlling the buffer by yourself. – Aritz Nov 08 '13 at 08:52
  • I tried the option of having multiple message component but it doesn't work with my layout. I am using templates in my app for the layout and would like to have all my messages being displayed in only one messages component. – Nasedo47 Nov 08 '13 at 09:05
  • Having multiple message component does not solve your issue. You have to put a `p:messages` tag in your template page, with `autoUpdate=true`. Try also removing `partialSubmit` and `async` attributes from your `remoteCommand` tags. – Aritz Nov 08 '13 at 09:24
  • 2
    I have managed to create a simple Vector that is used as a buffer for my messages and it works perfectly. Thanks for your help! – Nasedo47 Nov 08 '13 at 10:03
1

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
    }
}
Ömer Faruk Almalı
  • 3,792
  • 6
  • 37
  • 63