I am working on an application where users will be evaluating a piece of art and filling out a review form as they go along. They may spend a substantial amount of time on the form, so I want to save it automatically for the user, say every 5 minutes. I was hoping I could use javascript and set a timer for the 5 minutes and then have it basically execute the entire form via ajax and I could save the data to the database in case the user gets disconnected, etc. Unfortunately I cannot seem to find any way to do this. Getting the model updated with the data is not a problem, but I can’t figure out how to get it to invoke the method (similar to how the action normally would when it was submitted.) I don’t want or need it to re-render anything, just let me call a method to save the data. How can I do this?
Problems implementing the solution
I tried implementing the solution of the hidden command link but I’m getting some very strange behavior. I am not sure what is causing this. First, some background on the implementation. Form #1 creates a bean (None scoped) and puts it into the flash, then redirects to Form #2. Form #2 is the big form I was writing about, where I want to implement the auto-save. Form #2 has a ViewScoped bean. In the PostConstruct for this bean, it retrieves the value from flash, and populates a property field. So far so good. This works perfectly without the javascript. I can press the command button to submit the form, and all is well. However, when I introduce the javascript, when it executes I get a null pointer exception from the variable that should have been populated from the flash by the PostConstruct. How is this javascript interfering with that? Once I have populated the property of view scoped bean with the object, it should not matter if its removed from flash scope, right? FYI if I remove ONLY the javascript code and leave everything else it goes back to working fine when I press the button to submit.
Form #1
<h:form>
... bunch of form objects ...
<h:commandButton "Start New" action="#{someRequestScopedBean.someMethod"/>
</h:form>
code for someRequestScopedBean.Method:
public String someMethod() {
// bunch of logic here
FacesContext.getCurrentInstance()
.getExternalContext()
.getFlash()
.put("myFlashObj", myFlashObj);
return "form2?faces-redirect=true";
}
view scoped bean used in form 2:
@ManagedBean
@ViewScoped
public class someViewScopedBean {
//bunch of properties here
@PostConstruct
public void initialize() {
this.myObject = (MyObject) FacesContext.getCurrentInstance()
.getExternalContext()
.getFlash()
.get("myFlashObject");
public void saveDraft() {
// save to database
}
}
Form 2 page:
<h:outputScript library="javax.faces" name="jsf.js"/>
<h:form id="myForm">
... whole bunch of fields here ...
... real button for user to submit ...
<h:commandButton value="Submit myForm"
action="#{someViewScopedBean.save}" />
... hidden button for auto-save by javascript ...
<h:commandLink id="hiddenSaveDraft" style="display: none;"
action="#{someViewScopedBean.saveDraft}" >
<f:ajax execute="@form" />
</h:commandLink>
<script>
function saveDraft() {
document.getElementById('qForm:hiddenSaveDraft').onclick();
window.setTimeout('saveDraft()',15000);
}
saveDraft();
</script>
</h:form>
I figured out the final solution, and the cause of problems I had implementing it the first time around. It had to do with WHEN the javascript was being fired. The script code was being fired at the exact point I placed the
<script></script>block, which was before the page was completely loaded and probably before the DOM was complete. This was causing all kinds of nasty, including duplicate invocations of@PostConstruct.I fixed it by using a javascript event listener to fire when the page was completely loaded. This is important because I am using facelets templating, and I didn’t have access to the
<h:body onload=attribute. The listener is a useful and elegant solution. The script block can be placed anywhere in the page. Here is what the script block looks like:This will invoke the hidden button every 5 minutes to save the form.