Search code examples
jsfprimefacesdialogcommandbutton

p:commandbutton action doesn't work inside p:dialog


I have a p:dialog and there is a panel inside it. The problem is "Save" button's action method is not working. It doesn't even calls the method. I can reach the method def. with ctrl+lm so there is no problem with method name.

<h:body>
    <h:form id="createAppUserForm" prependId="false">
      ....
      <p:dialog id="newRoleDialogId"
                  header="Add New Role"
                  resizable="true"
                  draggable="true"
                  widgetVar="newRoleDetailsDialog"  
                  appendToBody="true"
                  >
            <p:panel id="newRoleDialogPanel">
                <p:panelGrid id="newRoleDialogPanelGrid" columns="2" style="width: 100%" styleClass="panelGridWithoutBorder">
                    <h:outputText value="Role Name :"/>
                    <p:inputText value="#{createAppUserController.selectedRole.name}"/>
                    <h:outputText value="Role Desc :"/>
                    <p:inputText value="#{createAppUserController.selectedRole.description}"/>
                </p:panelGrid>
                <center>
                    <p:commandButton value="Save"
                                     update="roleListDataTable newRoleDialogPanelGrid growlCreateAppUser"
                                     oncomplete="if (!args.validationFailed) newRoleDetailsDialog.hide()"                                     
                                     action="#{createAppUserController.saveNewRole()}"/>
                    <p:commandButton value="Cancel"                                         
                                     immediate="true"
                                     onclick="newRoleDetailsDialog.hide()" />
                </center>
            </p:panel>
        </p:dialog>
       </h:form>
    </h:body>

Solution

  • The dialog, when used with an appendToBody/appendTo="@Body" must have its own form.

    <p:dialog>
        <h:form>
            ...
        </h:form>
    </p:dialog>
    

    Because, when the dialog is generated into HTML output, it's by JavaScript relocated to the end of HTML <body> which causes it to not be sitting in any form anymore. The generated HTML DOM tree ends up to look like this (use webbrowser's dev tools to see it):

    <body>
        ...
        <form id="createAppUserForm">
            ...
        </form>
        ...
        <div id="newRoleDialogId" class="ui-dialog ...">
            ...
        </div>
    </body>
    

    The appendToBody="true" plays a role in here. The end of body ensures easy and best cross browser compatibility of displaying a modal dialog by JavaScript.

    The same is true by the way for a p:overlayPanel with an appendTo...

    But also make sure there is, before 'moving' the p:dialog, there is not a nested h:form. So prevent

    <h:form>
       ...
    
        <p:dialog>
            <h:form>
                ...
            </h:form>
        </p:dialog>
    
       ...
    </h:form>
    

    Since although it ends up like

     <body>
        ...
        <form id="createAppUserForm">
            ...
        </form>
        ...
        <div id="newRoleDialogId" class="ui-dialog ...">
            <form>
               ...
            </form>
        </div>
    </body>
    

    it is initially invalid html

    See also: