How can I add FacesMessage during page load? Using @PostConstruct does not seem to work
Asked Answered
L

3

23

In a backing bean's @PostConstruct method, I make a call to an EJB which might return some messages that I want to display on the page via p:messages. However, even if I add the FacesMessages e.g. FacesContext.getCurrentInstance().addMessage(...), p:messages is not being updated with the FacesMessages.

If I instead invoke the call to the EJB on an action from the page (say a user clicks a button on the page which invokes a method that calls the EJB and then adds the FacesMessage(s)), then the messags show up using p:messages as expected.

How do I add Faces Messages during @PostConstruct and have them show up when the page is initially rendered?

Code:

Page1Controller.java:

@ManagedBean
public class Page1Controller
{
    @PostConstruct
    public void init()
    {
        FacesContext.getCurrentInstance().addMessage(null, 
            new FacesMessage("Test Message from @PostConstruct"));
    }

    public String getValue()
    {
            return "Some Value";
    }

    public void triggerMessage(ActionEvent event)
    {
            FacesContext.getCurrentInstance().addMessage(null, 
                    new FacesMessage("Test Message from Trigger Button"));      
    }

}

page1.xhtml

   <h:form>
        <p:messages showDetail="true" showSummary="true" autoUpdate="true"/>
        <h:outputText value="#{page1Controller.value}"/>
        <br/>
        <p:commandButton value="Trigger Message" 
                         actionListener="#{page1Controller.triggerMessage}"/>  
   </h:form>
Lawford answered 17/4, 2012 at 18:52 Comment(2)
Note-- if invoke the same method (to call the EJB, etc) using the f:event type="preRenderView" listener directive, then the FacesMessage is appropriately updated. Should I just do this?Lawford
Using Mojarra 2.1.7, Primefaces 3.2 (latest stable).Lawford
M
25

That can happen when the message component is rendered before the message is added.

In your specific example, the bean is referenced for the first time by the <h:outputText> component and thus constructed for the first time at that moment. But the <h:outputText> component appears in your specific example after the <p:messages> component, so the <p:messages> component is already rendered and thus it's too late to show the message.

You need to make sure somehow that the message is added before the message component is rendered. One way is using <f:viewAction>. It runs during INVOKE_APPLICATION phase which is before RENDER_RESPONSE phase. Thus it runs before any component is rendered. A perfect opportunity thus.

<f:metadata>
    <f:viewAction action="#{bean.onload}" />
</f:metadata>

public void onload() {
    // Add message here instead of in @PostConstruct.
}

See also:

Myxoma answered 17/4, 2012 at 19:59 Comment(5)
Thanks... is an alternative that in the @PostConstruct method, after I've added the FacesMessage, I then access the Messages component (via ViewRoot.findComponent) and tell the messages component to "rerender" itself? (Is this possible?) If possible, is this a better approach than preRenderView or moving output text above p:messages?Lawford
No, that's not possible. As to the proper approach, well that depends on the concrete functional requirement. Under what condition exactly do you need to add a faces message? When some request parameter is invalid or so? If so, you should rather have used <f:viewParam> for this. If not, then the <f:event type="preRenderView"> would be fine (which is better to be replaced by a more sane <f:viewAction> when you get the chance to upgrade to JSF 2.2 later).Myxoma
Now, this is a real frustration with JSF... It shouldn't be this hard to get a message displayed on the screen.Ollayos
I don't understand this :"the <p:messages> is rendered before the managed bean is constructed for the first time. It's then too late to add the message." Why is it too late ? Note that I don't have any issue with I'm just curious.Operative
@CED: Plain java 'example': Suppose you have an array with 5 elements and iterate over it and do a System.out.println of each (rendereing it) and then add an additional element. That won't be displayed then. And this is not a JSF specific problem like an 8 year old comment suggests. Plain JSP with EL and whatever js UI framework is used has the same problem.Anthropologist
W
2

You can collect the error and then display it at the end of loading the page using a remoteCommand of primefaces with autorun = true mode. In my case I have a viewScope and in xhtml I show the list of values that are loaded in the @PostConstruct. If an Exception is generated I will save it to the sample at the end of the page load if it exists using the remoteCommand.

private ArrayList<Exception> postConstucError = new ArrayList<>();

@PostConstruct
public void validarAcceso() {
    /**
     * verificar permisos a la vista de coeficientes
     */
    try {
            this.init() //load data;
        } catch (Exception e) {
        System.out.print(e.getMessage());
        this.postConstucError.add(e);
    }
}

 public void showPostConstructError() {
    try {
        for (int i = 0; i < this.postConstucError.size(); i++) {
            JsfUtil.addErrorMessage("Error al cargar datos iniciales: " + postConstucError.get(i).getMessage());
        }
    } catch (Exception e) {
        JsfUtil.addErrorMessage(e, "Error: showPostConstructError() " + e.getMessage());
    }
}

xhtml code

  <p:messages id="messages" showDetail="true" autoUpdate="true" closable="true"/> 
  <h:form>
        <p:remoteCommand id="rcomerror" name="showError" process="@this"  autoRun="true"
                         actionListener="#{mBPresentNinos.showPostConstructError()}" />  
    </h:form>
Winegrower answered 5/4, 2017 at 14:30 Comment(1)
Creative solution... And how does your showPostConstructError() method look like?Anthropologist
F
0

For me using preRenderView event to display message on form init was a messages-hell. So I created very simple "component" to keep static messages. For this example only one error message is supported.

staticMessage.xhtml:

<ui:fragment xmlns="http://www.w3.org/1999/xhtml"
             xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
             rendered="#{rendered}">
    <div id="staticMessage" class="ui-messages ui-widget" aria-live="polite">
        <div class="ui-messages-error ui-corner-all"><span class="ui-messages-error-icon"/>
            <ul>
                <li>
                    <span class="ui-messages-error-summary">#{value}</span>
                </li>
            </ul>
        </div>
    </div>
</ui:fragment>

Including messages:

 <ui:include src="/template/components/staticMessage.xhtml">
     <ui:param name="rendered"value="#{beanMB.staticMessagesRendered}"/>
     <ui:param name="value" value="Place your message here."/>
 </ui:include>
Fealty answered 24/4, 2018 at 9:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.