I have a field that has a required attribute. When I press the Accept button to save some data without entering any value in the field, an error message is displayed. So far so good.
But, if right after that I decide to click on the Cancel button, that error message overrides the confirmation message that is supposed to be displayed inside a <p:dialog/> element.
NOTE: If instead I use the <p:confirmDialog/> component, there seems to be no problem because, I guess, it uses a message="" attribute, no the <p:messages/> tag.
XHTML
<p:dialog>
<p:outputPanel>
<h:form>
<h:outputText value="Field:"/>
<p:inputText id="field" value="" type="text" required="true" requiredMessage="You must complete the field" />
<p:growl id="messages" showDetail="true"/>
<p:commandButton id="dialogCancel" value="Cancel" oncomplete="confirmCancelDialog.show();" actionListener="#{controller.addCloseWarn}" />
</h:form>
</p:outputPanel>
</p:dialog>
<h:form>
<p:dialog id="confirmCancelDialog" header="Warning!" widgetVar="confirmCancelDialog" modal="true" >
<p:messages id="closeMessage" showDetail="true" autoUpdate="true" />
<p:commandButton id="confirm" value="Accept" onclick="..." />
<p:commandButton id="decline" value="Cancel" onclick="..." />
</p:dialog>
</h:form>
Bean controller
public void addCloseWarn(ActionEvent actionEvent) {
FacesContext.getCurrentInstance().addMessage("closeMessage", new FacesMessage(FacesMessage.SEVERITY_WARN, null,
"Are you sure you want to leave the page?"));
}
Problem with Cancel button is that your form is submitted and validation is executed. You can add process="#this" attribute to commandButton, so other parts of form will not be processed and your addCloseWarn method will be executed.
I would also add that this is probably not standard use of message tag. It is used to show errors, warning and successful messages, not confirmation questions. So use confirmDialog or use standard dialog with just ordinary text and OK - Cancel buttons.
Related
I need your assistant in showing the error messages in the proper messages component. In the form, I am using the messages component tag twice. The first messages component tag which is called messages, I am showing any error to the user that is validated from the main form.
Where I am also having a dialog in the same form that will validate if a variable is blank or empty, if it is blank,the error message should be shown in the messagesPDFST which is in the dialog not in the main form. Currently any error it is shown in the first messages component which is messages even if the error is in the dialog. Here is the code:
<h:form id="Requests">
<p:messages id="messages" showDetail="true" autoUpdate="true" closable="true"/>
<p:dialog id="Certificate1" header="Certificate" widgetVar="Certificate1" modal="true">
<div align="right">
<p:messages id="messagesPDFST" showDetail="true" autoUpdate="true" closable="true" />
<p:commandButton value="Download" ajax="false" actionListener="#{hrd.PDFSTAR}" >
<p:fileDownload value="#{hrd.fileSTAR}" />
</p:commandButton>
</div>
</p:dialog>
</h:form>
and the Bean Code:
public void PDFSTAR() {
if (refNo== null || refNo.equals("")) {
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error!", "Please enter the Reference No."));
}
}
To display a FacesMessage for a specific component, you first need to
Queue the message, for the specific component:
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error!", "Please enter the Reference No."));
The first argument where you have null, is where you should have the client-side id of the component you're looking to target
Specify the for attribute on the message component, limiting display of messages to that component only.
Putting it all together, you should now have
FacesContext.getCurrentInstance().addMessage("Requests:Certificate1:downloadButton", new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error!", "Please enter the Reference No."));
Where "downloadButton" is the id of the <p:commandButton/>
<p:messages for="downloadButton" id="messages" showDetail="true" autoUpdate="true" closable="true"/>
<p:commandButton id="downloadButton" value="Download" ajax="false" actionListener="#{hrd.PDFSTAR}" >
<p:fileDownload value="#{hrd.fileSTAR}" />
</p:commandButton>
Unrelated to the actual problem
There's no need to be using <p:messages/> in the dialog, if the message is to be displayed for a single component only - <p:message/> will work just as well
I have jsf page with TabView... Together 3 tabs. Tabs calls dialogs with some forms for creating elements. Dialogs are placed in external .xhtml page.
First tab is on "main" page (code placed below) and growl message called from validation bean shows well on the page. Second tab is calling growl message but from button placed on form from external file with dialogs... No errors occurs but the growl message didn't show at all.
My "main" view:
<ui:composition>
<p:growl id="growl" showDetail="true" life="4000" />
<h:form id="systemSettingsForm">
<p:tabView id="ticketSettingsTab" rendered="true" widgetVar="tabViewVar">
<p:tab id="tab0">
...
</p:tab>
<p:tab id="tab1">
<ui:include src="/WEB-INF/tags/dialogs.xhtml" />
<p:commandButton value="Add" oncomplete="PF('addDialog').show()" update=":systemSettingsForm:ticketSettingsTab:addingDlg" />
...
</p:tab>
</p:tabView>
</h:form>
</ui:composition>
</h:body>
Dialogs view looks like (nothing special as I think...):
<p:dialog id="addPositionDlg" header="Add position" widgetVar="addDialog">
<h:panelGrid id="addingDlg" columns="3" >
<p:outputLabel value="Position name :"/>
<p:selectOneMenu id="selectType" value="#{positionsTableView.positionType}" >
<f:selectItem itemLabel="Select one option" itemValue="" />
<f:selectItems value="#{positionsTableView.types}" />
</p:selectOneMenu>
<p:outputLabel style="width:1px;"/>
<p:outputLabel value="Prefix :" />
<p:inputText id="addPrefix" value="#{positionsTableView.newPosition.prefix}" />
<pe:tooltip for="addPrefix" showEvent="focus" hideEvent="blur">...</pe:tooltip>
//few simillar inputs, not even validated
<center>
<p:commandButton value="Add" actionListener="#{positionsTableView.addByPrefix}" ajax="true"
update=":systemSettingsForm:ticketSettingsTab:mappingTable" oncomplete="PF('addDialog').hide();"/>
</center>
</h:panelGrid>
</p:dialog>
Growl message is added in actionListener invoked by command button in dialog form (#{positionsTableView.addByPrefix}) and this bean is in the same package as previous one (this working) and message is added in the same manner, which is:
public static void addMessage(String title, String message) {
FacesContext context = FacesContext.getCurrentInstance();
context.addMessage(null, new FacesMessage(title, message));
}
Both beans calling messages are session-scoped.
I have searched here and tried to google solutions but none was successful...
Please help.
MY SOLUTION:
At command button which invokes the message to be displayed on growl element I have updated whole tabView menu and it works.Maybe it's not the best solution, but updating growl id causes an error (it was not visible from dialog file).
I've got a problem as described in the title.
Small description of the problem is as following:
I have button which is used to open dialog. Then, inside that dialog, there is button which opens another dialog on top of the first one. After clicking second button I want method from controller to be called but nothing happens. Value in h:outputText is read properly, so I guess it is not a problem with connection controller->view.
I'm using:
Spring web 3.1.2.RELEASE
JSF 2.2.10
Primefaces 5.1
Code:
beans.xml
<bean id="testController" class="test.TestController" />
TestController.java
public class TestController implements Serializable
{
private static final long serialVersionUID = 7028608421091861830L;
private String test;
public TestController()
{
test = "abc";
}
public void testMethod()
{
test = "cba";
}
public String getTest()
{
return test;
}
}
test.xhtml
<h:panelGrid columns="1" cellpadding="5">
<p:commandButton value="Basic" type="button" onclick="PF('dlg1').show();" />
</h:panelGrid>
<p:dialog widgetVar="dlg1">
<h:outputText value="Resistance to PrimeFaces is futile!" />
<h:panelGrid columns="1" cellpadding="5">
<p:commandButton value="Basic" type="button" onclick="PF('dlg2').show();" />
</h:panelGrid>
<p:dialog widgetVar="dlg2">
<h:outputText value="#{testController.test}" />
<p:commandButton value="Call method" type="button" actionListener="#{testController.testMethod}" />
</p:dialog>
</p:dialog>
What I tried:
adding appendToBody="true" to each p:dialog
changing from p:commandButton to p:button
changing from actionListener to action
but nothing helps.
I would be grateful for any help or advice of what can be the reason of not calling given method.
There are 3 problems.
You're nesting <p:dialog> components. This doesn't make sense. Separate them.
A <p:dialog> must have its own <h:form>, particularly when you explicitly use appendToBody="true" or appendTo="#(body)", otherwise nothing can be submitted because JavaScript would relocate the dialog out of its position in the HTML DOM tree to the end of body, causing it to not be sitting in a form anymore.
A <p:commandButton type="button"> acts as a "click" button, not as a submit button. Remove that attribute from submit buttons.
All in all, this is how it should look like:
<h:form>
<h:panelGrid columns="1" cellpadding="5">
<p:commandButton value="Basic" type="button" onclick="PF('dlg1').show();" />
</h:panelGrid>
</h:form>
<p:dialog widgetVar="dlg1">
<h:form>
<h:outputText value="Resistance to PrimeFaces is futile!" />
<h:panelGrid columns="1" cellpadding="5">
<p:commandButton value="Basic" type="button" onclick="PF('dlg2').show();" />
</h:panelGrid>
</h:form>
</p:dialog>
<p:dialog widgetVar="dlg2">
<h:form>
<h:outputText value="#{testController.test}" />
<p:commandButton value="Call method" actionListener="#{testController.testMethod}" />
</h:form>
</p:dialog>
OK. I guess I found a way to fix this problem.
It seems that the problem was:
type="button"
I deleted it from the list of attributes of each button and now it works even without h:form. Thanks for help.
I'm trying to block a <p:commandButton> which is displayed on a <p:confirmDialog> as follows.
<p:confirmDialog id="confirmDeleteDialog" widgetVar="confirmDelete" message="Message" closeOnEscape="true" appendTo="#(body)" closable="true">
<p:blockUI block="confirm" widgetVar="blockUI">
<h:outputText value="Demo"/>
</p:blockUI>
<p:commandButton id="confirm" value="Yes" onstart="PF('blockUI').show()" oncomplete="PF('blockUI').hide();"/> <!--Use PF('confirmDelete').hide() to dismiss the dialog.-->
<p:commandButton id="decline" value="No" onclick="PF('confirmDelete').hide()" type="button" />
</p:confirmDialog>
<p:commandButton oncomplete="PF('confirmDelete').show()"/>
This blocks Yes button (one with the id="confirm") held by <p:confirmDialog>.
The message to be displayed on the confirm dialog is dynamic. It is fetched from the associated backing bean based on certain conditions. Therefore, the dialog is required to be updated before it is displayed.
To update the dialog before it is displayed, the update attribute of <p:commandButton> is set as follows.
<p:commandButton oncomplete="PF('confirmDelete').show()" update="confirmDeleteDialog"/>
This of course, displays a desired message on the dialog dynamically (which is not demonstrated here for the sake of simplicity) but doing so prevents the <p:blockUI> from functioning - it does not block the <p:commandButton>, if the dialog is updated by it.
Neither errors on the browser console nor exceptions on the server terminal are seen.
What is the way of blocking a button held by a in addition to displaying a dynamic message on the dialog?
EDIT :- the answer provided by me is cross-mark.
As noted in the answer given by me, <p/pe:blockUI> needs to be updated, when the <p:commandButton> held by the confirm dialog is clicked, it takes some time to block the button - <p:commandButton> after it is clicked. In the meanwhile, the button can be clicked (either deliberately or accidently) before the initial request completes that may cause duplicate submits which is certainly against the use of <p/pe:blockUI>.
Nothing is preventing a user in my workaround, from clicking the button multiple times before it is blocked by <p/pe:blockUI>. Therefore, the answer provided by me is considered to be cross-mark.
Perhaps the way to meet this requirement is completely different.
EDIT 2:- the actual scenario.
//A view scoped bean.
//A list of selected rows in <p:dataTable>.
private List<WishUtils>selectedValues;
//The actual message to be displayed on the dialog.
private String deleteMsg;
//Associated with the rendered property of the "Yes" button on the dialog.
private boolean renderedYesButtonDelete=true;
//The header message/text of the dialog.
private String messageBoxHeader;
//The no button text (its value).
private String noButtonTextDelete="No";
//Getters and setters as required.
public void confirmDelete(ActionEvent actionEvent) {
if(selectedValues!=null && !selectedValues.isEmpty()) {
renderedYesButtonDelete=true;
noButtonTextDelete="No";
deleteMsg="A long message about a delete prompt from a resource bundle";
messageBoxHeader="Confirm Delete";
} else {
noButtonTextDelete="OK";
renderedYesButtonDelete=false;
deleteMsg="Please select the rows you want to delete";
messageBoxHeader="Confirm Item Select";
}
}
public void delete(ActionEvent actionEvent) {
if(actionEvent.getComponent().getId().equals("confirmDeleteMultiple")) {
//Delete the selected rows.
} else {
//Notify an error (generally never going to be executed).
}
}
The message box goes here :
<p:confirmDialog id="confirmDialogDeleteMultiple" widgetVar="confirmDeleteUIWidget" header="#{wishManagedBean.messageBoxHeader}" closeOnEscape="true" appendTo="#(body)" closable="true">
<p:blockUI id="blockDelete" block="confirmDeleteMultiple" widgetVar="blockDeleteUIWidget"/>
<p:remoteCommand name="blockDeleteCommand" update="blockDelete" process="#this"/>
<p:commandButton id="confirmDeleteMultiple"
value="#{messages['confirmdialog.yes']}"
onclick="blockDeleteCommand();"
onstart="PF('blockDeleteUIWidget').show();"
oncomplete="PF('blockDeleteUIWidget').hide();PF('confirmDeleteUIWidget').hide()"
actionListener="#{wishManagedBean.delete}"
process="#this"
rendered="#{wishManagedBean.renderedYesButtonDelete}"
update="a list of components to be updated"/>
<p:commandButton id="declineDeleteMultiple"
value="#{wishManagedBean.noButtonTextDelete}"
onclick="PF('confirmDeleteUIWidget').hide()"
type="button"/>
<f:facet name="message">
<p:outputPanel>
<h:outputFormat value="#{wishManagedBean.deleteMsg}" escape="false"/>
</p:outputPanel>
</f:facet>
</p:confirmDialog>
The <p:blockUI> indeed fails when the entire dialog is updated. Looks like just another bug. It works when you explicitly call the PrimeFaces.cw(...) "create widget" script of the <p:blockUI> component (exactly that script which you see in generated HTML output representation of <p:blockUI>) on complete of the dialog update.
Given those IDs,
<h:form id="formId">
<p:confirmDialog id="confirmDialogId">
<p:blockUI id="blockId" ... />
<p:commandButton id="confirmButtonId" ... />
The following oncomplete should do it:
<p:commandButton update="confirmDialogId"
oncomplete="PrimeFaces.cw('BlockUI','blockUI',{id:'formId:blockId',block:'formId:confirmId'});PF('confirmDialogId').show()" />
That explains why the <p:remoteCommand> trick works as it under the covers basically re-generates the <p:blockUI> component along with that PrimeFaces.cw(...) call, although it fires an unnecessary ajax request. It might be worth reporting an issue to PrimeFaces guys, telling that PrimeFaces.cw(...) of <p:blockUI> isn't properly executed when the confirm dialog widget is updated.
Another workaround would be to explicitly ajax-update only the parts you'd like to update instead of the entire dialog. That works for me. The header and the message attributes of the <p:confirmDialog> (and those of many other PrimeFaces components) supports being defined via <f:facet> on the attribute name. This allows you to wrap it in a <h:outputText> (or <h:panelGroup>) so that you can just update it individually. This way you don't need to update the entire dialog and the block UI keeps working as intented.
<p:confirmDialog ...>
<f:facet name="header">
<h:outputText id="confirmDeleteDialogHeader" value="#{bean.header}" />
</f:facet>
<f:facet name="message">
<h:outputText id="confirmDeleteDialogMessage" value="#{bean.message}" />
</f:facet>
<h:panelGroup id="confirmDeleteDialogContent">
<p:blockUI ... />
<p:commandButton ... />
<p:commandButton ... />
</h:panelGroup>
</p:confirmDialog>
<p:commandButton ...
update="confirmDeleteDialogHeader confirmDeleteDialogMessage confirmDeleteDialogContent"
oncomplete="PF('confirmDelete').show()" />
This requires the <p/pe:blockUI> to be updated, when the <p:commandButton> displayed on the confirm dialog is clicked.
The <p:blockUI> can be updated onclick using the <p:remoteCommand> (onstart also works).
The code goes here.
<p:confirmDialog id="confirmDeleteDialog" widgetVar="confirmDelete" message="Message" closeOnEscape="true" appendTo="#(body)" closable="true">
<p:blockUI id="blockConfirm" block="confirm" widgetVar="blockUI"/>
<p:remoteCommand name="confirmCommand" update="blockConfirm"/>
<!--Add PF('confirmDelete').hide() to oncomplete to dismiss the dialog, when this button is clicked.-->
<p:commandButton id="confirm" value="Yes" onclick="confirmCommand();" onstart="PF('blockUI').show()" oncomplete="PF('blockUI').hide()"/>
<p:commandButton id="decline" value="No" onclick="PF('confirmDelete').hide()" type="button" />
</p:confirmDialog>
<p:commandButton oncomplete="PF('confirmDelete').show()" update="confirmDeleteDialog" value="Submit"/>
I am not able to display a p:dialog with prefilled values for a p:inputText component:
<p:dialog modal="true" widgetVar="editPersonDlg" header="Edit Person" width="350">
<h:form id="editPersonForm">
<h:panelGrid columns="2">
<h:outputLabel for="editFirstName" value="First Name:" />
<p:inputText id="editFirstName" value="#{personBean.selectedPerson.firstName}" />
<p:commandButton value="Save" type="Button" actionListener="#{personBean.edit}"
oncomplete="editPersonDlg.hide()"/>
<p:commandButton value="Cancel" type="Button" oncomplete="editPersonDlg.hide()"/>
</h:panelGrid>
</h:form>
</p:dialog>
When debugging I see that personBean#selectedPerson is effectively returning a not-null Person, with not-null names. Person#getFirstName is effectively returning a not-null name. However FirstName and LastName don't appear in the inputText boxes of the Dialog.
It could be beacause you don't update the dialog before you open it.
For example:
You initialize the personBean.selectedPerson by selecting it in p:dataTable and then you want to edit it by clicking on p:commandButton which opens the "edit" dialog. You have to update this dialog so the component can fetch actual data. Try something like this for the button which opens the dialog:
<p:commandButton value="Edit" oncolmplete="editPersonDlg.show()" update=":formInWhichIsDialog:dialogID" />
Let me know if it worked, problem can be somewhere else but this is the most common thing.
Hope it helped !