I am currently coding a Web Application with JSF 2.3 and primefaces 11. Because we are trying to minimize duplicate code we are refactoring Facelets with composite components. This works for the most part very well, but I've encountered a problem:
I want to use a dialog (coded in a composite) to add a new contact in another composite component which displays a table of all contacts. The dialog works fine but the listener method of the confirm button is never called and I cannot figure out why.
The Dialog:
<div xmlns="http://www.w3.org/1999/xhtml"
xmlns:p="http://primefaces.org/ui"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:composite="http://xmlns.jcp.org/jsf/composite">
<composite:interface>
<composite:attribute name="contact" type="de.my.project.model.extensions.ContactOption" required="true"/>
<composite:attribute name="confirmAction" required="true" method-signature="java.lang.Void confirm()"/>
<composite:attribute name="widgetvar" type="java.lang.String" required="true"/>
<composite:attribute name="appendTo" type="java.lang.String" default=""/> </composite:interface>
<composite:implementation>
<!-- Dialog to add a contact. -->
<p:dialog id="#{cc.attrs.widgetvar}" widgetVar="#{cc.attrs.widgetvar}"
modal="true" appendTo="#{cc.attrs.appendTo}">
<p:ajax event="open" update="#{cc.attrs.widgetvar}-form" resetValues="true"/>
<p:ajax event="close" immediate="true" ignoreAutoUpdate="true"/>
<h:form id="#{cc.attrs.widgetvar}-form">
<!-- Hidden button, only to prevent the form submit on enter press. -->
<p:commandButton styleClass="hidden"/>
<div class="grid mt-1">
<!-- Name input -->
<div class="col-4 flex align-items-center">
<p:outputLabel value="#{msg['name']}"/>
</div>
<div class="col-8 flex align-items-center">
<p:inputText value="#{cc.attrs.contact.label}"
required="true"
maxlength="255"/>
</div>
<!-- Phone number -->
<div class="col-4 flex align-items-center">
<p:outputLabel value="#{msg['phone-number']} "/>
</div>
<div class="col-8 flex align-items-center">
<p:inputText value="#{cc.attrs.contact.telephone}" maxlength="255"
required="true"/>
</div>
</div>
<!-- Confirm/cancel buttons -->
<div class="flex justify-content-end mt-2">
<p:commandButton value="cancel" onclick="PF('#{cc.attrs.widgetvar}').hide()"
styleClass="ui-button-danger mr-2" icon="pi pi-times"/>
<p:commandButton value="confirm"
update="@form" icon="pi pi-check"
action="#{cc.attrs.confirmAction}"/>
</div>
</h:form>
</p:dialog>
</composite:implementation>
</div>
The table composite:
<div xmlns="http://www.w3.org/1999/xhtml"
xmlns:p="http://primefaces.org/ui"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:my="http://java.sun.com/jsf/composite/project"
xmlns:composite="http://xmlns.jcp.org/jsf/composite"
>
<composite:interface>
<composite:attribute name="contactsTableId" required="true"/>
<composite:attribute name="contactController" required="true"
type="de.my.project.controller.AbstractContactOptionController"/>
</composite:interface>
<composite:implementation>
<p:dataTable value="#{cc.attrs.contactController.contactOptions}" var="item" id="#{cc.attrs.contactsTableId}"
sortMode="multiple">
<!--Contact's label-->
<p:column headerText="#{msg['label']}" sortBy="#{item.label}"
filterBy="#{item.label}"
filterMatchMode="contains" styleClass="w-auto">
<p:outputLabel value="#{item.label}"/>
</p:column>
<!--Contact's number-->
<p:column headerText="telephone" sortBy="#{item.telephone}"
filterBy="#{item.telephone}"
filterMatchMode="contains">
<p:outputLabel value="item.telephone"/>
</p:column>
<!-- Action column -->
<p:column rendered="#{request.isUserInRole('MRCC_MAINTAINER')}">
<f:facet name="header">
<p:outputLabel value="actions" styleClass="block"/>
<!-- add button -->
<p:commandButton icon="pi pi-plus"
value="#{msg['add-upper']}"
actionListener="#{cc.attrs.contactController.initAddContact}"
immediate="true"
styleClass="block ui-button-success mt-2"/>
</f:facet>
</p:column>
</p:dataTable>
<my:contactDialog widgetvar="contacts-table-add-contact"
appendTo="body"
title="#{msg['contact-add']}"
confirmAction="#{cc.attrs.contactController.confirmContactOptionAdd()}"
contact="#{cc.attrs.contactController.contact}"/>
</composite:implementation>
The bean with the initAddContact()
and the confirmContactOptionAdd()
methods:
@Named
@ViewScoped
public class ContactOptionController extends AbstractContactOptionController {
/**
* ID/widget var of the add dialog.
*/
@Getter
private static final String ADD_DIALOG_ID = "c";
/**
* The {@link ContactOption}s.
*/
@Getter
@Setter
private List<ContactOption> contactOptions;
/**
* The filtered {@link ContactOption}s.
*/
@Getter
@Setter
private List<ContactOption> filteredContactOptions;
@Inject
private ContactOptionService contactOptionService;
/**
* Contact option to add.
*/
@Getter
@Setter
private ContactOption contact;
/**
* Initialises the contact add dialog.
*/
public void initAddContact() {
contact = new ContactOption();
contactType = null;
PrimeFaces.current().executeScript("PF('" + ADD_DIALOG_ID + "').show()");
}
/**
* Delegates a new {@link ContactOption} to Persistence.
*/
@Override
@DisplayDomainFault
public void confirmContactOptionAdd() {
contactService.add(contact);
contact = new ContactOption();
PrimeFaces.current().executeScript("PF('" + ADD_DIALOG_ID + "').hide()");
}
}
The problem seems to be connected to the nested composites or theappendTo
parameter because I can get it to work, when the dialog is not called in another composite and is included in <Ui:define name="bodyEnd">
with appendTo
not being set. I hope somebody can help.