Primefaces duplicate messages in dialog and page

4.7k Views Asked by At

I'm working with Primefaces 5.2, JSF 2.2.10, and Tomcat 7.0.

I have a user page, with its personal data, and one button that allows that user to change its password through a dialog.

In the page, I'm using a p:messages component (with id="messages"), and other p:messages component (with id="messagesDialog") inside the dialog.

To change its password, the user has to introduce its current password, and the new one.

When the dialog fields fail their validation I'd like to show the errors inside the dialog messages component ("messagesDialog"), but not in the page messages component ("messages").

How could I get it?

Detail image

I've tried to use a field inside the controller that it's true when the dialog is opened, and false when it's closed. The page messages ("messages") has the rendered attribute with that field. But it didn't work out.

This is the code I'm using:

XHTML page:

<p:messages id="messages" closable="true" 
    rendered="#{not userController.dialogChangePasswordOpened}" />

<h:form id="formUserData">
    <p:outputPanel styleClass="panelGrey">
        <!-- User data... -->

        <p:panelGrid styleClass="noBorders">
            <p:row>
                <p:column styleClass="colLabel">
                    <p:outputLabel id="labelChangePassword" for="changePassword"
                        value="#{msg['label.password']}"></p:outputLabel>
                </p:column>
                <p:column styleClass="colInput">
                    <p:commandButton id="changePassword"
                        value="#{msg['user.changePassword']}" 
                        actionListener="#{userController.openDialogChangePassword}"
                        update="messages formChangePassword">
                    </p:commandButton>
                </p:column>
            </p:row>
        </p:panelGrid>
    </p:outputPanel>
</h:form>


<!-- Dialog change password -->
<h:form id="formChangePassword">
    <p:dialog id="dlgChangePassword" modal="true"
        header="#{msg['user.changePassword.title']}"
        widgetVar="dlgChangePassword" styleClass="popUpPassword" closable="false">

        <p:messages id="messagesDialog" closable="true" autoUpdate="true"
            globalOnly="false" />
        <p:panelGrid styleClass="noBorders" id="panelChangePassword">
            <p:row>
                <p:column>
                    <p:outputLabel id="labelOldPassword" for="oldPassword"
                        value="#{msg['label.oldPassword']}" />
                </p:column>
                <p:column>
                    <p:password id="oldPassword"
                        value="#{userController.oldPassword}" required="true"
                        feedback="false">
                        <p:ajax update="messagesDialog" event="keyup"></p:ajax>
                    </p:password>
                </p:column>
            </p:row>
            <p:row>
                <p:column>
                    <p:outputLabel id="labelNewPassword" for="newPassword"
                        value="#{msg['label.newPassword']}" />
                </p:column>
                <p:column>
                    <p:password id="newPassword"
                        value="#{userController.newPassword}"
                        match="checkNewPassword"
                        required="true" feedback="true">
                    </p:password>
                </p:column>
            </p:row>
            <p:row>
                <p:column>
                    <p:outputLabel id="labelCheckNewPassword" for="checkNewPassword"
                        value="#{msg['label.checkNewPassword']}" />
                </p:column>
                <p:column>
                    <p:password id="checkNewPassword"
                        value="#{userController.newPassword}" required="true"
                        feedback="false">
                    </p:password>
                </p:column>
            </p:row>
            <p:row>
                <p:column rowspan="2">
                    <p:commandButton id="btnOK" value="#{msg['button.ok']}"
                        update="messages messagesDialog"
                        actionListener="#{userController.changePassword}">
                    </p:commandButton>
                    <p:commandButton id="btnCancel"
                        value="#{msg['button.cancel']}" immediate="true"
                        actionListener="#{userController.closeDialogChangePassword}" />
                </p:column>
            </p:row>
        </p:panelGrid>
    </p:dialog>
</h:form>

UserController:

private boolean dialogChangePasswordOpened;

public boolean isDialogChangePasswordOpened() {
    return dialogChangePasswordOpened;
}

public void setDialogChangePasswordOpened(boolean dialogChangePasswordOpened) {
    this.dialogChangePasswordOpened = dialogChangePasswordOpened;
}

public void openDialogChangePassword() {
    dialogChangePasswordOpened = true;
    resetChangePasswordFields();
    RequestContext.getCurrentInstance().execute("PF('dlgChangePassword').show()");
}

public void closeDialogChangePassword() {
    dialogChangePasswordOpened = false;
    resetChangePasswordFields();
    RequestContext.getCurrentInstance().execute("PF('dlgChangePassword').hide()");
}

public void resetChangePasswordFields() {
    this.newPassword = "";
    this.oldPassword = "";
}

public void changePassword(ActionEvent actionEvent){
    try{
        usuarioService.changePassword(this.userAuthenticated.getName(),this.oldPassword, this.newPassword);
        FacesContext.getCurrentInstance().addMessage("messages",
                new FacesMessage(FacesMessage.SEVERITY_INFO,
                        "Password has been changed correctly.", null));
        closeDialogChangePassword();
    } catch (Exception e) {
        FacesContext.getCurrentInstance().addMessage("messagesDialog",
                new FacesMessage(FacesMessage.SEVERITY_ERROR,
                        "Error while changing the password.", null));
    }
}

Thanks.

4

There are 4 best solutions below

2
On

You should try adding a globalOnly="true" to the p:messages with id="messages". You can remove the globalOnly to the p:messages inside your dialog because it defaults to false anyway.

To make this work, change

FacesContext.getCurrentInstance().addMessage("messages",
                new FacesMessage(FacesMessage.SEVERITY_INFO,
                        "Password has been changed correctly.", null));

to this:

FacesContext.getCurrentInstance().addMessage(null,
                new FacesMessage(FacesMessage.SEVERITY_INFO,
                        "Password has been changed correctly.", null));

Placing null on the first parameter (the id of the component) means that the message will be global and will be caught by p:messages with globalOnly="true".

Good Luck!

3
On

You should only update the messages you want to display: currently you do 'update="messages messagesDialog"' should be update="messagesDialog"

0
On

The only way I have found to eliminate is with multiple targeted messages in the dialog and turning off the autoUpdate on the main page (and adding the update when I think that I will need it on a component by component basis.

In your dialog you would do something like this:

<p:messages id="messagesDialog1" autoUpdate="true" for="oldPassword" />
<p:messages id="messagesDialog2" autoUpdate="true" for="newPassword" />
<p:messages id="messagesDialog3" autoUpdate="true" for="checkNewPassword" />

Notice that you do not have a "catch-all" bin - but they would still look proper to the user as they would all be right under each other in any order of priority that you saw fit.

On the page proper you still have the messages going into a catch-all, but you will have to update it specifically. I like to separate mine between the growler and the message boxes like this:

<p:growl id="growler" showDetail="true" autoUpdate="false" sticky="false" severity="info,warn" />
<p:messages id="messages" showDetail="true" autoUpdate="false" closable="true" severity="error,fatal" />

Then down in the components when I process an action that might generate an error message I simply update them:

<p:commandButton value="Do something" action=#{something.fun} update="myPanel growler messages" />

The bottom line is that I don't think that you can actually do what you are trying to do, but you can make it look like is is doing it by doing it a different way to accomplish the same outcome.

I hope that helps.

0
On

Use redisplay = "false" in the p:messages in the main page. But for this to work, your dialogs must be included before the page's p:message .

You could use a template to define a region to put all the dialogs and set them with ui:define.

Template:

<ui:insert name="dialogs"/>
<p:messages autoUpdate="true"  redisplay="false" id="msgs" showDetail="true" showSummary="false" closable="true"/>
<ui:insert name="content" />

Page:

<ui:define name="dialogs">
 .... include your dialogs here .....
</ui:define>
<ui:define name="content">
 <h:form>
    ...
 </h:form>
</ui:define>

see this link: Don't redisplay messages already shown in dialog in <p:messages autoUpdate="true">