I need your help and guidance on fixing the issue of showing the dialog after refreshing the form by using ajax. I am having a dataTable that it is showing a number of requests and onclick of the commandButton view, a dialog will be shown that contains some information. And onclose of the dialog, the form will be refreshed and the dataTable will get the updated information from the database. However for the first time, the dialog will be shown and the form will be refreshed by closing the dialog.
If I tried to click again on the view commandButton on the second time, the commandButton will not show any dialog and will not seem to do an action. Even the log it is not showing anything.
Here is my xhtml:
<h:form id="Requests">
<p:dataTable id="PendingRequests" var="hr" value="#{hrd.pendingRequests}"
filteredValue="#{hrd.filteredPendingRequests}">
<p:column headerText="Req. Date" sortBy="#{hr.requestDate}"
filterMatchMode="contains" filterBy="#{hr.requestDate}">
<h:outputText value="#{hr.requestDate}" />
</p:column>
<p:column>
<f:facet name="header">View</f:facet>
<p:commandButton id="submitbutton" update=":Requests:#{hr.dialogueName} "
oncomplete="PF('#{hr.certificateDialogue}').show()" icon="ui-icon-search"
title="View">
<f:setPropertyActionListener value="#{hr}" target="#{hrd.selectedRequest}" />
</p:commandButton>
</p:column>
</p:dataTable>
<p:dialog id="CertificateDialog" header="Cert1" widgetVar="CertificateDialog">
<p:ajax event="close" update=":Requests" listener="#{hrd.UpdateDatatable}" />
</p:dialog>
</h:form>
This method will be called onclose of the dialog .The UpdateDatatable method code is:
listPendingRequests.clear();
listPendingRequests = new ArrayList<PendingRequests>();
PreparedStatement ps = null;
String sql = "";
try {
con = DBConn.getInstance().getConnection("jdbc/hr");
// SQL Statement
ps = con.prepareStatement(sql);
ResultSet result = ps.executeQuery();
while (result.next()) {
PendingRequests pendingList = new PendingRequests();
requestDate = result.getString("REQ_DT");
pendingList.setRequestDate(requestDate);
listPendingRequests.add(pendingList);
RequestContext.getCurrentInstance().update("Requests");
}
} catch (Exception e) {
System.err.print(e);
e.printStackTrace();
}
Dialogs have a special treatment in HTML DOM and JavaScript. You should never be ajax-updating any of the dialog's parents while still in the same view. Otherwise you potentially end up with duplicate dialogs and conflicts in JS. Moreover, the strong recommendation is to give each dialog its own form as in <p:dialog><h:form> to avoid confusion and misuse by starters, but as you'd like to use ajax close event on it, you can't get around to use <h:form><p:dialog> instead. However, you placed other stuff in the very same form and you're even ajax-updating it. This is not right.
Tie the form to the dialog itself and don't put other stuff on it. In other words, split the current form.
<h:form id="requests">
<p:dataTable ...>
...
</p:dataTable>
</h:form>
<h:form>
<p:dialog ...>
<p:ajax event="close" ... update=":requests" />
</p:dialog>
</h:form>
Related
I need to execute a call using Ajax, but first, I also have to make a validation on my backing bean's method. Based on a boolean returned by this method, the ajax call will be executed (returned true) or not (returned false, for example).
To be more specific and to give a touchable example, I am trying to open a dialog after I click on a button.
This is my .xhtml code:
<h:form id="tableVendasNaoPagas">
<p:dataTable value="#{relatorioVendaMB.vendasNaoPagas}" var="venda" rowKey="#{venda.codigo}" emptyMessage="Nenhuma venda encontrada" selection="#{relatorioVendaMB.vendasAPagar}">
<f:facet name="header">Tabela de Venda</f:facet>
<p:column selectionMode="multiple" style="width:16px;text-align:center"/>
<p:column headerText="Código" sortBy="#{venda.codigo}">
<h:outputText value="#{venda.codigo}"/>
</p:column>
<!--Other columns-->
<f:facet name="footer">
<p:commandButton id="btnAbreDialogEfetuarPagamento" value="Efetuar Pagamento" process="#form" update=":formConfirmarPagamento, :formAux:growlglobal, #this" oncomplete="if (#{relatorioVendaMB.abreDialogEfetuaPag()}) { PF('dialogConfirmarPagamento').show() }"/>
</f:facet>
</p:dataTable>
</h:form>
.
.
.
<h:form id="formConfirmarPagamento">
<p:dialog id="dialogConfirmarPagamento" ...>
</h:form>
.java:
public boolean abreDialogEfetuaPag () {
if (vendasAPagar == null || vendasAPagar.isEmpty()){
MensagensUtil.mensagemAviso("Atenção", "selecione ao menos uma venda");
return false;
} else {
return true;
}
}
The dialog should be displayed only if at least one item from the table has been selected (the list vendasAPagar is not empty).
The call to the method on the backing bean isn't required, but it would be convenient, because I could add a message to a <p:growl> and give a feedback to the user (Like here: MensagensUtil.mensagemAviso("Atenção", "selecione ao menos uma venda");)
I've tried many different approaches, but they didn't work either:
<p:commandButton id="btnAbreDialogEfetuarPagamento" value="Efetuar Pagamento">
<p:ajax process="#form" update=":formConfirmarPagamento, :formAux:growlglobal" disabled="#{!relatorioVendaMB.abreDialogEfetuaPag()}" oncomplete="PF('dialogConfirmarPagamento').show()"/>
</p:commandButton>
<p:commandButton id="btnAbreDialogEfetuarPagamento" value="Efetuar Pagamento" process="#form" update=":formConfirmarPagamento, :formAux:growlglobal, #this" oncomplete="#{not empty relatorioVendaMB.abreDialogEfetuaPag()} ? PF('dialogConfirmarPagamento').show() : return false"/>
...And many others. In some, the dialog would always be shown and the method would not be executed, in others the method would be executed but the dialog would never be shown...
Could someone give me a hint, a solution and, if possible, explain how Ajax works in this type of situation. Thanks in advance.
Here is what we do...
First don't use a boolean use AJAX and JSF to your advantage instead.
In your JAVA code you can mark validationFailed if you do not want to show the dialog.
public void abreDialogEfetuaPag () {
if (vendasAPagar == null || vendasAPagar.isEmpty()){
MensagensUtil.mensagemAviso("Atenção", "selecione ao menos uma venda");
FacesContext.getCurrentInstance().validationFailed();
}
}
Have your button only show the dialog if validation did not fail. PrimeFaces hands back the value of that validationFailed flag in the "args" of every AJAX request.
<p:commandButton oncomplete="if (!args.validationFailed) PF('dialogConfirmarPagamento').show();" />
You can enable the button already with the selection. The button now is only a click button without bean connection.
<p:datatable....
<p:ajax event="rowSelect" partialSubmit="true" process="#this" update="btnAbreDialogEfetuarPagamento" />
<p:commandButton id="btnAbreDialogEfetuarPagamento" rendered="#{relatorioVendaMB.abreDialogEfetuaPag()}" value="Efetuar Pagamento" type="button" onclick="PF('dialogConfirmarPagamento').show() }"/>
I need your help in refreshing the dataTable component on closing the Dialog. I tried many ways to refresh the dataTable, but it is not retrieving the updated record, unless I have refreshed the full page by clicking on the "Go" button which is next to the URL in the address bar.
The xhtml code:
<h:form id="Requests">
<h:panelGroup id="table">
<p:fieldset id="Pendings" legend="Pending Requests">
<div id="refresh">
<p:dataTable id="PendingRequests" var="hr" value="#{hrd.pendingRequests}" paginator="true" rows="15" paginatorTemplate="{RowsPerPageDropdown} {FirstPageLink} {PreviousPageLink} {CurrentPageReport} {NextPageLink} {LastPageLink}" rowsPerPageTemplate="5,10,15" paginatorPosition="bottom" filteredValue="#{hrd.filteredPendingRequests}">
<p:column headerText="Req. Date" sortBy="#{hr.requestDate}" filterMatchMode="contains" filterBy="#{hr.requestDate}" >
<h:outputText value="#{hr.requestDate}"/>
</p:column>
<p:column>
<f:facet name="header">View</f:facet>
<p:commandButton id="submitbutton" update=":Requests:#{hr.dialogueName} "
oncomplete="PF('#{hr.certificateDialogue}').show()" icon="ui-icon-search" title="View">
<f:setPropertyActionListener value="#{hr}" target="#{hrd.selectedRequest}"/>
</p:commandButton>
</p:column>
</p:dataTable>
</div>
</p:fieldset>
</h:panelGroup>
<p:dialog id="CertificateDialog" header="Cert1" widgetVar="CertificateDialog" >
<p:ajax event="close" update=":Requests" listener="#{hrd.UpdateDatatable}"/>
</p:dialog>
</h:form>
I tried to update the dataTable only, but it is not refreshing. Although, I have tried to update the full form using #form and #all and again the dataTable was not refreshed.
The updateDataTable method:
public void UpdateDatatable(CloseEvent event) {
RequestContext.getCurrentInstance().update(":Requests");
}
When you try to update a component from ManagedBean using RequestContext.update() you should not use the relative component id, because you'd have nothing to relate to.
To fix your problem remove : before Requests in your listener.
RequestContext.getCurrentInstance().update("Requests");
If you feel updating a component from managed bean, increases cohesion. You can use a p:remoteCommand can call if from your javascript any time you want.
<p:remoteCommand name="updateTable" process="#this" partialSubmit="true" update=":Results" />
And you can call the above remoteCommand from javascript or in your case from dialog as below:
<p:dialog onhide="updateTable()">
...
</pdialog>
My suggestion is to move p:dialog out of the h:form in which you placed the dataTable. Because In future if you get into situation where if need to update the h:form from p:dialog while its still open, updating the very own h:form in which the p:dialog is placed, would cause the dialog the p:dialog to close abruptly.
If you your p:dialog out for h:form then you might not need the UpdateDatatable() listener itself. update from your p:ajax would do the job for you.
I have a jsf code like this. When user finish to input name and comment and press 'Enter', the content can show in 'Content........' this part.
<h:form id="all_comments">
Content........
</h:form>
<h:form id="submit_comment">
<h:inputText id="name_box" value="#{ManagedBean.user}"/>
<h:inputText id="content_box" value="#{ManagedBean.comment}" />
</h:form>
I want to use ajax finish it, and I try like this:
<h:form id="all_comments">
Content........
</h:form>
<h:form id="submit_comment">
<h:inputText id="name_box" value="#{ManagedBean.user}"/>
<h:inputText id="content_box" value="#{ManagedBean.content}">
<f:ajax event="keydown"
listener="#{ManagedBean.addComment}"
execute="comment_name_box comment_content_box"
rendered=":all_comments" />
</h:inputText>
</h:form>
But I failed, can I achieve that when user press 'Enter', data will be processed by ManagedBean, then using ajax update the page.
You've got some points missing:
You need to send an AJAX call only when enter key was pressed, so you need to bind <h:inputText> tag's onkeydown attribute with JavaScript code for that;
<f:ajax> attribute is render, not rendered;
submit_comment form should be rerendered as well, so that new data will be presented to the user, and the placeholders have to be refreshed in AJAX listener as well.
Here is the solution when you want to submit the second form on that event:
<h:form id="all_comments">
Content........
</h:form>
<h:form id="submit_comment">
<h:inputText id="name_box" value="#{managedBean.user}"/>
<h:inputText id="content_box" value="#{managedBean.content}"
onkeydown="return (event.keyCode == 13);">
<f:ajax event="keydown"
listener="#{managedBean.addComment}"
execute="#form"
render="#form :all_comments" />
</h:inputText>
</h:form>
with
public void addComment(AjaxBehaviorEvent abe) {
comments.add(new Comment(user, content));
user = "";
content = "";
}
I have basically two problems.
When I press the button in Form1, it is working fine, but I can't see the messages from FacesContext.
Another problem is in Form2. When I press the button only once, it goes to the server but nothing happens, no submit. But when I press it on second time, it is working fine. There is of course the same problem like in form one, that I can't see the messages from FacesContext. Could you please help and tell me what is causing that or is there another solution for having multiple forms inside one page?
<p:tabView>
<p:tab title="Form1">
<h:form id="form1">
<p:inputText id="txtInput" value="#{controller.selected.defaultLayout}" />
<h:commandButton value="Submit other form" action="#{controller.createMenu()}">
<f:ajax execute="#form" render="#form" />
</h:commandButton>
</h:form>
</p:tab>
<p:tab title="Form2">
<h:form id="form2">
<p:inputText id="txtInput2" value="#{controller.selected.defaultTheme}" />
<h:commandButton value="Submit other form" action="#{controller.createMenu2()}">
<f:ajax execute="#form" render="#form" />
</h:commandButton>
</h:form>
</p:tab>
</p:tabView>
Controller:
public String createMenu() {
Menu menu = current.getMenuMenuId();
try {
//current.getMenuMenuId().setMenuCreated(true);
//getFacade().edit(current);
JsfUtil.addSuccessMessage(ResourceBundle.getBundle("resources/Bundle").getString("MenuCreated"));-----> never visible!
return "";
} catch (Exception e) {
JsfUtil.addErrorMessage(e, ResourceBundle.getBundle("resources/Bundle").getString("PersistenceErrorOccured"));
return null;
}
}
EDIT: I took the outer -tags off and then the page is not working anymore, then I get the error message:
javax.faces.FacesException: <f:ajax> contains an unknown id ':form2' - cannot locate it in the context of the component j_idt77
at com.sun.faces.renderkit.html_basic.AjaxBehaviorRenderer.getResolvedId(AjaxBehaviorRenderer.java:285)
at com.sun.faces.renderkit.html_basic.AjaxBehaviorRenderer.appendIds(AjaxBehaviorRenderer.java:272)
at com.sun.faces.renderkit.html_basic.AjaxBehaviorRenderer.buildAjaxCommand(AjaxBehaviorRenderer.java:214)
at com.sun.faces.renderkit.html_basic.AjaxBehaviorRenderer.getScript(AjaxBehaviorRenderer.java:86)
at javax.faces.component.behavior.ClientBehaviorBase.getScript(ClientBehaviorBase.java:103)
Thank you!
Sami
Nesting forms is not valid html. This will cause unexpected behavior. Remove the outer form and see which of your issues persist.
UPDATE:
Primefaces tabview works without form as well. However if you have input elements and command buttons inside your tabs you need a form. But this is a html requirement and not PF specific. You should remove the outer form only and not the inner forms. Please update your question with your current version.
I have
<h:form>
<h:commandLink action="#{my_fake_ajax_link}">
<h:outputText value="Link" />
<f:ajax render=":mydiv" />
</h:commandLink>
</h:form>
<h:panelGroup layout="block" id="mydiv">
<h:form>
<h:commandLink action="#{mybean.delete(0)}">
<h:outputText value="Here" />
<f:ajax render="#form" />
</h:commandLink>
</h:form>
</h:panelGroup>
When I click once on "my_fake_ajax_link", then I have to click twice on "delete" link. This is only an example. I don't have this real case. I have multiple forms on a page and I can't just add all of them in a single form.
I checked what the problem is and it is:
When you click on "my_fake_ajax_link", the mydiv refreshs with ajax as it should.
ViewState of the refreshed form on ajax is missing.
How can I add the ViewState? How can I make it work without using only one form? This looks like an JSF bug to me. I don't want to refresh that div automatically with
jQuery("#mydiv").load(document.location.href);
but I will in my worst case possible.
This is a known problem in the auto-included jsf.js library of JSF which handles ajax responses. See also JSF spec issue 790 which is fixed in the upcoming JSF 2.3. In the meanwhile, with JSF 2.0/2.1/2.2, you have to explicitly specify the ID of the other <h:form> inside the render attribtue to trigger proper addition of the view state.
<h:form>
<h:commandLink action="#{my_fake_ajax_link}">
<h:outputText value="Link" />
<f:ajax render=":mydiv :mydivForm" />
</h:commandLink>
</h:form>
<h:panelGroup layout="block" id="mydiv">
<h:form id="mydivForm">
<h:commandLink action="#{mybean.delete(0)}">
<h:outputText value="Here" />
<f:ajax render="#form" />
</h:commandLink>
</h:form>
</h:panelGroup>
No, this does not cause any overhead or markup duplication in the ajax response. Alternatively, use OmniFaces fixviewstate.js.
See also:
Communication in JSF 2.0 - Ajax rendering of content which contains another form
h:commandButton/h:commandLink does not work on first click, works only on second click
Workaround for that:
First you need to set an onevent handler for the button that sends the ajax request:
<h:form id="newComment">
<h:commandButton id="saveNewComment" action="#{postBean.actionSaveNewCommentAjax}" value="#{rb['speichern']}">
<f:ajax execute="#form" render=":commentsBox" onevent="function(data) { fixOtherFormsViewState(data, 'newComment') }" />
</h:commandButton>
</h:form>
Then you need to declare this javascript methods that will take the correct viewState value and add it to all forms which doesn't have it:
<h:outputScript>
function fixOtherFormsViewState(data, goodFormId) {
if (data.status != "success") {
return;
}
var viewState = jQuery("#" + goodFormId + " input[name='javax.faces.ViewState']").val();
jQuery("form:not(:contains(input[name='javax.faces.ViewState']))").each(function (idx, elem) {
var form = jQuery(elem);
var input = jQuery("<input type='hidden' name='javax.faces.ViewState' />").val(viewState);
form.append(input);
});
}
</h:outputScript>