Prevent Double Submit on JSF Dialog AJAX CRUD - java

Just would like to know why do I encounter this behavior in my application.
I used primefaces for the UI and almost all of my pages follows this pattern. I heavily used AJAX in all of my CRUD
operations and using dialogs to show it to the user.
<ui:composition template="myTemplate.xhtml">
<ui:define name="content">
<ui:include
src="/pages/CreateDialog.xhtml" />
<ui:include
src="/pages/UpdateDialog.xhtml" />
<ui:include
src="/pages/DeleteDialog.xhtml" />
</ui:define>
</ui:composition>
My only concern is that, after doing CRUD stuff in my dialogs and user accidentally clicks F5 or refresh on the browser,
FF/Chrome and other browser always mentioned
To display this page, Firefox must send repeat action...
Obviously this will cause double submit. Previously I used Post-Redirect-Get in this scenario in older apps but since this
is AJAX JSF update then I cannot do this.
What's the workaround for this and is this normal? I thought AJAX actions should not trigger again during browser refresh.
Help?
UPDATE
I am opening my dialog with this code
<p:commandButton value="Add"
onclick="createWidget.show();"
update=":CreateForm"
action="#{MyBean.add}"
/>
My create dialog uses this
<p:dialog header="Create">
<h:form id="CreateForm" prependId="false">
<p:commandButton value="Add" icon="ui-icon-plus"
actionListener="#{MyBean.add}"
update=":messageGrowl"
oncomplete="closeDialogIfSucess(xhr, status, args, createWidget 'createDialogId')"/>
</h:form>
</p:dialog>
I actually am following the pages from this site...Full WebApplication JSF EJB JPA JAAS

Already experienced a few times that having JavaScript errors in callback methods ends up in such behavior. I was able to reproduce your problem which is disappeared after correcting the callback signature:
oncomplete="closeDialogIfSucess(xhr, status, args, createWidget, 'createDialogId')"
accordingly to you JavaScript function signature:
function closeDialogIfSucess(xhr, status, args, createWidget, dialogid)
(Sure if your JavaScript call has only 3 parameters then correct the oncomplete call)
Unrelated: I guess that you are using this function to close a specific dialog. Another way doing it would be assigning widgetVar attribute to your dialog:
<p:dialog id="testDialog" header="Create" widgetVar="createWidget">
<h:form id="CreateForm" prependId="false">
...
</h:form>
</p:dialog>
The widgetVar object will represent your dialog in the callback function so you can close it by call the hide() function of dialog:
function closeDialogIfSucess(xhr, status, args, createWidget) {
if(args.validationFailed || !args.loggedIn) {
jQuery('#testDialog').effect("shake", { times:3 }, 100);
} else {
createWidget.hide();
}
}

Related

onShow event fires unnecessarily during each page reloading

I have an issue with making a menu in my project using Primefaces. Actually, this menu will get me a possibility to show some small dialogs with settings for the workspace (by clicking on menu items). Each dialog should have data lazy loading from database. Unfortunately, when I include my dialogs in the page (single xhtml file or a couple xhtml files with ui:include), an event onShow happens on each page reload and this is wrong and provoke too many unnecessary requests to the database.
Here is an example:
UI part
<h:form id="form1">
<p:menubar id="mainMenu">
<p:submenu label="Main menu" icon="ui-icon-document">
<p:menuitem value="My settings" onclick="mySettingsWv.show()" url="#" />
</p:submenu>
</p:menubar>
</h:form>
<p:dialog id="mySettingsDlg" header="My Settings" widgetVar="mySettingsWv" resizable="false"
closable="true" modal="true" showEffect="fade" hideEffect="explode" dynamic="true"
closeOnEscape="true" onShow="#{mybean.onShow()}">
<h:outputLabel value="Settings dialog" />
</p:dialog>
ManagedBean part:
#ManagedBean (name = "mybean")
#ViewScoped
public class MyBean {
public static Logger log = Logger.getLogger(MyBean.class);
public void onShow() {
log.info("Method onShow is being called on each page reloading, but dialog still has not been shown");
}
}
If I use action "onclick" for <p:menuitem> for manual calling the necessary method, it still executes it for each page reloading. Also if I try use actionListener, action attributes it don't work. <p:ajax> cannot be attached to <p:menuitem>.
What should I do in that case? What is wrong can be in my code?
Have a look at the primefaces documentation for p:dialog (same problem with p:menuitem and onclick). There the documentation says about onShow:
Client side callback to execute when dialog is displayed. (emphasis added)
That means that you can specify a javascript function there, but it does not work that way to specify a action on your backingbean which is called everytime the dialog is shown. What happens in your case is the following: #{mybean.onShow()} is evaluated only when the file is parsed (i.e. the p:dialog is rendered into the HTML) and then the value which is returned by the method is inserted there (i.e. the empty String).
To fix this you have to define a javascript callback which makes the call on the bean. You can do this by using p:remoteCommand:
<p:remoteCommand name="onShow" action="#{mybean.onShow}"
partialSubmit="true" process="#this"/>
And then specify this callback as the onShow attribute:
<p:dialog id="mySettingsDlg" ...
onShow="onShow()">

Ajax callback onstart works even if input is invalid

I have input fields and a command button inside an <h:form>. The input fields are required, and the validation error messages are showing well if something invalid is entered. At the end of the form, I have the following button:
<p:commandButton value="" ajax="true" update="foormm"
icon="ui-icon-check" actionListener="#{bean.sayHello}"
onstart="dialog.show()"
oncomplete="handleAjaxResponse(xhr, status, args)">
</p:commandButton>
The problem is, the dialog is showing regardless of validation. I thought the Ajax request was made iff inputs are validated. What's wrong here? Why does onstart get triggered even though the required fields are empty?
I use JSF 2.0, Primefaces 3.0 and Weblogic 12.1 with Eclipse.
Any help appreciated.
In other words, you want to show the dialog only when no validation errors have occurred after the postback? The onstart doesn't take that into account at all. It's invoked right before the ajax postback is invoked.
You'd need to approach it differently. The <p:dialog> has a visible attribute which can take an EL expression evaluating to a boolean outcome. You could make use of it:
<p:dialog id="dialog" visible="#{some condition}">
The #{some condition} can at its simplest be a combination of FacesContext#isPostback() and FacesContext#isValidationFalied(). So, if it's a postback and the validation has not failed, then the dialog should be visible.
<p:dialog id="dialog" visible="#{facesContext.postback and not facesContext.validationFailed}">
You just have to let your button update that dialog as well:
<p:commandButton value="" ajax="true" update="foormm dialog"
icon="ui-icon-check" actionListener="#{bean.sayHello}"
oncomplete="handleAjaxResponse(xhr, status, args)">
</p:commandButton>
Alternatively, you can also let the dialog's visible condition depend on some bean property which you set in the action(listener?) method:
<p:dialog id="dialog" visible="#{bean.saidHello}">

How to disable/re-enable JSF component using javascript? [duplicate]

This question already has an answer here:
How to disable/enable JSF input field in JavaScript?
(1 answer)
Closed 3 years ago.
I'm developing a Java EE application (JSF2 + richfaces 3.3.3 + facelets).
I want to disable my h:selectOneMenu when loading my page, and when it finishes loading (using the function onload()), I want to re-enable my component.
I have something like this:
<ui:define name="infosHead">
<script type="text/javascript">
window.onload = function() {
document.getElementById("forme1_myform:valueCh").disabled = false;
alert("here");
}
</script>
</ui:define>
<ui:define name="infosBody">
<h:form id="forme1_myform" target="_blank">
<h:selectOneMenu id="valueCh" value="#{mybean.value}" disabled="true" >
<f:selectItems value="#{mybean.values}" />
<a4j:support event="onchange"
ajaxSingle="true"
limitToList="true"
reRender="id1,id2,...."
ignoreDupResponses="true"
action="#{mybean.actionme}"
oncomplete="getId();"/>
</h:selectOneMenu>
</h:form>
</ui:define>
this is working fine.
But my bean is getting nothing (mybean.value == null).
It's like he thinks that the component is still disabled.
how can I make this works ?
The problem is that you are enabling your component only on the client side. On the server side it will always be disabled="true". To make this work you must :
a. Assign the disabled property of your component to a managed bean property that will be initially 'true'
disabled="#{myController.valueChDisableStatus}"
b. Inside your h:form insert window.onload = callScript
c. Finally, in the myController#someAction method set the valueChDisableStatus property to false
I cant check the solution right now, but I believe it will work fine.
I found this solution.
<ui:define name="infosHead">
<script type="text/javascript">
window.onload = function() {
updateName(false); // sets 'disabled' from true to false
}
</script>
</ui:define>
<ui:define name="infosBody">
<h:form id="forme1_myform" target="_blank">
<h:selectOneMenu id="valueCh" value="#{mybean.value}" disabled="#{mybean.render}" >
<f:selectItems value="#{mybean.values}" />
<a4j:support event="onchange"
ajaxSingle="true"
limitToList="true"
reRender="id1,id2,...."
ignoreDupResponses="true"
action="#{mybean.actionme}"
oncomplete="getId();"/>
</h:selectOneMenu>
</h:form>
<a4j:form>
<!-- this is where it's going to reRender my component -->
<a4j:jsFunction name="updateName" reRender="valueCh">
<a4j:actionparam name="param1" assignTo="#{mybean.render}" />
</a4j:jsFunction>
</a4j:form>
</ui:define>
and this is the content of mybean:
public class MyBean implements Serializable {
private List<SelectItem> values;
private String value;
private Boolean render = true; // the page loads with element disabled
public void actionme(){...}
//getters & setters
}
Neither of these solutions worked for me.
While setting the element's disabled attribute from a managed bean value worked fine, and changing it's value via javascript also worked fine, once the form is submitted the "someaction" method is still not reached in order to "set the valueChDisableStatus property to false". In addition I found that even though my managed bean had an isDisabled() and a setDisabled(boolean value), the setDisabled() method was never called after submitting the form (of course), though the isDisabled() was. So I was unable to find a way using these solutions to change the managed beans "disabledElement" value to false.
I did find a solution that did work for me though at this link:
http://blog.michaelscepaniak.com/jsf-you-cant-submit-a-disabled-form-element
Basically it suggests that by default all the elements should be "enabled" from the JSF perspective, and that all enabling and disabling should be done via Javascript. It does point out the the client and server states are out of sync temporarily in this solution... but it works very well for my scenario regardless of the temporary mismatch.

Is there a way to not sending a whole web form when click a button?

I will try to be as brief as possible, please stay with me here
"A.jsf" -> managed bean : bean
"#{bean.list}": will take us to B.jsf
<p:growl id="msgs" showDetail="true"/>
<h:form id="myform1" enctype="multipart/form-data">
<p:panel header="Upload" style="font-size: 11px;">
<h:panelGrid columns="2" cellpadding="10">
<h:outputLabel value="Drawing:" />
<p:fileUpload fileUploadListener="#{bean.handleFileUpload}" update="msgs" allowTypes="*.*;"/>
</h:panelGrid>
<p:commandButton ajax="false" immediate="true" id="back" value="Back" action="#{bean.list}"/>
<p:commandButton ajax="false" id="persist" value="Persist" action="#{bean.handleRevision}" />
</p:panel>
</h:form>
Then the handleFileUpload()
if(!upload){
FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error", "You do not have permission to upload.");
FacesContext.getCurrentInstance().addMessage(null, msg);
}
...
"B.jsf" -> managed bean: bean2
...
<p:growl id="msgs" showDetail="true"/>
...
When I click upload, it give me a growl error message "You do not have permission to upload.", which is good. But then when I click "Back", which will take me to B.jsf, I see the growl message "You do not have permission to upload." again. What seem to be happening is as I click the "Back", I send other form request to upload, which then generated the same error message, which then being displayed at B.jsf. Is there a way to fix this, beside putting the "Back" button into an empty form, because now I have two buttons standing on top of each others, instead of side by side. I try to do this:
FacesContext.getCurrentInstance().addMessage("tom", msg);
hoping that it would send to component with id="tom", so then the growl with id=msgs, would not get load, but no luck. I try to turn the upload flag on when I click the Back button, but the web form get requested before the method that handle the back navigation get called.
It is not as brief as I want it to be, therefore I want apologize for it :D
beside putting the "Back" button into an empty form, because now I have two buttons standing on top of each others
The HTML <form> is by default a block element. HTML block elements are by default been placed in a new line. You actually want to make it an inline element. You can do this using display: inline; in CSS.
Back to the actual problem, it however surprises me that the fileUploadListener method is called in spite of the immediate="true" in the p:commandButton. I tried to reproduce this and I can confirm this. But I wouldn't expect it to happen. Normally the immediate="true" on a button is the solution to skip submitting of the "whole" form (at least, skip the UIInput components without this attribute). Further investigation learnt me that the p:fileUpload isn't an UIInput component at all and that the listener is fired during apply request values phase instead of validations or update model values phase. So this behaviour is fully predictable, but imo still an oversight in the design.
Since the p:fileUpload requires ajax="false" on the p:commandButton component, you can on the other hand also just remove it from the back button so that it fires an ajaxical request and hereby skips the fileUploadListener being called.
Actually, putting the button in a different form sounds like an excellent solution. The reason the buttons don't align any more is that the new starting <form> element starts on its own line. You should be able to prevent this by adding form { display: inline; } to your CSS file.
That said, if you have some leftover error messages that you want to get rid of, you can do this in the initializing method of your backing bean (if you have one). The following works peachily:
public void clearErrorMessages() {
//it may get messy to debug why messages are swallowed
logger.debug("clearing messages, coming from " + new Exception().getStackTrace()[1]);
Iterator iter = FacesContext.getCurrentInstance().getMessages();
while (iter.hasNext()) {
FacesMessage msg = (FacesMessage) iter.next();
logger.debug("clearing message: " + msg.getDetail());
iter.remove();
}
}
The disadvantage here is that any errors that occur between submitting the form and initializing the backing bean of the target page are also swallowed.

JSF 2.0: Delay rendering (composite) component's contents until AJAX-call re-renders it

My goal is to dynamically load the contents of a component in JSF 2.0. The use case is this: user clicks some button, which opens a modal panel with some heavy-contents via AJAX. Due to its' heaviness, I want to delay loading it until/if user actually needs it. If the user closes this panel, it is not actually removed from DOM, just faded out. If the user clicks the initialization button again, the previously loaded panel is shown.
I know that I can prevent rendering of contents using rendered="#{cc.attrs.visibilityState == 'hidden'}" and that I can re-render the component via JSF AJAX call. However, how can I adjust the attributes of a composite component on-the-fly so that the second time around the component would actually render?
1) I know that I could do:
<h:outputLink>Foo
<f:ajax event="click" render="theComponentIWantToUpdate" listener="#{someBean.someMethod()}" />
</h:outputLink>
And then programmatically adjust theComponentIWantToUpdate attributes (to change value of #{cc.attrs.visibilityState}) so that it would actually render with full contents. But how to actually do that?
2) Also, the problem is that I don't want to update (re-render) theComponentIWantToUpdate each time the button is pressed, only the first time (see the business case). How can I set an evaluation for <f:ajax /> call for this? It has the disabled attribute, but it only orders whether or not to actually render the AJAX-handler (not evaluated each time the link is pressed).
3) Furthermore, I probably want to do some custom javascript first when the link is clicked and only execute AJAX request via javascript using jsf.ajax.request(). However, that function doesn't support providing listener attribute so I don't how to execute a backing bean method with raw javascript jsf.ajax.request() call? There is actually a similar question without suitable answers (see JSF 2.0 AJAX: jsf.ajax.request to call method not only rerender an area of page).
A partial solution:
Here is my link that sends an AJAX-request (inside a composite component):
<h:form>
<h:outputLink styleClass="modlet-icon">
<f:ajax event="click" render=":#{cc.clientId}:modalWindow:root" listener="#{modalWindowBean.enableContentRendering(cc.clientId, 'modalWindow')}" />
</h:outputLink>
</h:form>
The listener calls this method:
public class ModalWindowBean {
...
public void enableContentRendering(String clientId, String windowId) {
UIComponent component = FacesContext.getCurrentInstance().getViewRoot().findComponent(clientId + ":" + windowId);
component.getAttributes().put("contentRenderingEnabled", true);
}
}
Here is my target to be rendered:
<modalWindow:modalWindow id="modalWindow">
<quickMenu:quickMenuOverlay id="quickMenuOverlay" />
</modalWindow:modalWindow>
ModalWindow simply wraps the target into a nice looking window panel. Within ModalWindow:
<composite:implementation>
<h:outputScript library="component/modalWindow" name="modalWindow.js" target="head" />
<h:outputStylesheet library="component/modalWindow" name="ModalWindow.css" />
<h:panelGroup id="root" layout="block" styleClass="modalWindow hide">
<ui:fragment rendered="#{cc.attrs.contentRenderingEnabled}">
...all the wrapping elements with <composite:insertChildren /> within it
<script type="text/javascript">
// Fade it in
var win = ModalWindow.getInstance('#{cc.clientId}'); // this gets the instance, available everywhere
win.position(#{cc.attrs.left}, #{cc.attrs.top});
win.resize(#{cc.attrs.width}, #{cc.attrs.height});
win.fadeIn();
</script>
</ui:fragment>
</h:panelGroup>
<ui:fragment rendered="#{!cc.attrs.contentRenderingEnabled}">
<script type="text/javascript">
// Initialize modalWindow when it is rendered for the first time
var win = new ModalWindow('#{cc.clientId}'); // will be publicly available through ModalWindow static methods
</script>
</ui:fragment>
</composite:implementation>
The problem? AJAX-request is sent each time user clicks the button (so the window is reloaded and faded in each time). I need to be able to control when/if the AJAX-request is actually sent.
All this stuff makes me miss Apache Wicket, although I'm not sure how I would do this with it anyway :)

Categories