I have a rather long, tabbed XPage form to be populated by visitors, and I'd like to auto-save in the backend every few minutes.
The entire form represents a managed bean, and I can populate/save a Domino document from the bean values using a button manually just by calling...
portfolio.save(sessionScope.unid,false)
I located this excellent post by Jeremy Hodge (great how the older tips stand the test of time!) that explains the technique...
http://xpages.info/XPagesHome.nsf/Entry.xsp?documentId=88065536729EA065852578CB0066ADEC
I then found a forum response from Paul Calhoun that describes the use of the technique with a timer to fire off regular document saves...
I have implemented the technique on my XPage, but the SSJS simply isn't firing. I've even placed 'onStart' and 'onComplete' events to validate everything is working as expected. The counter works, the console shows the timer and the alerts appear every 5 seconds or so... but the SSJS simply isn't initiated.
My eventHandler that does the work...
<xp:eventHandler event="onfubar" id="autoSave" submit="false">
<xp:this.action><![CDATA[#{javascript:portfolio.save(sessionScope.unid,false);}]]>
</xp:this.action>
</xp:eventHandler>
... and the event handler that triggers the regular save (yes, I know that I've butchered the 'executeOnServer' command and manually added the additional parameters - just trying to isolate the issue)...
<xp:eventHandler event="onClientLoad" submit="false">
<xp:this.script><![CDATA[
var executeOnServer = function () {
// must supply event handler id or we're outta here....
if (!arguments[0])
return false;
// the ID of the event handler we want to execute
var functionName = arguments[0];
// OPTIONAL - The Client Side ID that you want to partial refresh after executing the event handler
var refreshId = (arguments[1]) ? arguments[1] : "@none";
var form = (arguments[1]) ? XSP.findForm(arguments[1]) : dojo.query('form')[0];
// OPTIONAL - Options object contianing onStart, onComplete and onError functions for the call to the
// handler and subsequent partial refresh
var options = (arguments[2]) ? arguments[2] : {};
// Set the ID in $$xspsubmitid of the event handler to execute
dojo.query('[name="$$xspsubmitid"]')[0].value = form.id + ':' + functionName;
XSP._partialRefresh("post", form, refreshId, options);
}
var x = 0;
var form = document.forms[0];
t = new dojox.timing.Timer(3600);
t.onTick = function() {
x+=1;
console.info(x);
if(x > 4){
x = 0;
console.info("reset");
executeOnServer('autoSave','@none',{
onStart: function() { alert('starting!'); },
onComplete: function() { alert('complete!'); },
onError: function() { alert('DANGER WILL ROBINSON!'); }
});
}
}
t.onStart = function() {
console.info("Starting timer");
}
t.start();]]></xp:this.script>
</xp:eventHandler>
I know that I must be missing something, and I'm probably staring straight at it, but I simply can't figure out where I've gone wrong.
Thanks very much for any insight into the issue (and a point in the right direction!).
It can actually be way simpler than the snippets you found (you can drop all of that)... Consider the following bean:
public class PortfolioBean implements Serializable {
private static final long serialVersionUID = 1L;
private Map<String, Object> data = new HashMap<String, Object>();
public Map<String, Object> getData() {
return data;
}
public void autoSave(String unid, Boolean something) {
System.out.println("The unid is " + unid + " and something is " + something);
data.put("stamp", "autosave at " + new Date());
}
}
And the following xsp source:
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
<xp:div id="myForm" style="padding: 20px;">
<xp:eventHandler event="onautosave" id="eventAutoSave"
submit="false" action="#{javascript:portfolio.autoSave(sessionScope.unid, false)}" />
<p>Stamp</p>
<xp:inputText id="inputStamp" value="#{portfolio.data.stamp}" size="60" />
</xp:div>
<xp:scriptBlock value="
XSP.addOnLoad(function() {
window.setInterval(function() {
XSP.partialRefreshPost('#{id:inputStamp}', {
execId: '#{id:myForm}',
params: { '$$xspsubmitid' : '#{id:eventAutoSave}' },
onComplete: function() { console.log('complete at ' + new Date()); }
});
}, 5000);
});" />
</xp:view>
I created a myForm
container to create an "anchor" and narrow down the portion of the page that gets evaluated, read submitted or executed (best practice in view of best performances). myForm
is therefore the execution id. It's fundamental that the execution id belongs to a an element that contains the event handler you want to fire. If, for example, I placed the onautosave
event outside the myForm
div the event would not be fired. If you couldn't care less (bad!) you could remove the execId: '#{id:myForm}'
property passed to the object in the XSP.partialRefreshPost
method call and, at this point, it would work regardless of the xsp components arrangement.
In addition, with the XSP.partialRefreshPost
you can decide whether you want to partial refresh any element of the page or not. I put #{id:inputStamp}
so that you could see the input field getting filled out with the text resulting from firing the autosave. If you know that you don't have to display any DOM change that would result from the server-side method evaluation you can specify an empty string (like this XSP.partialRefreshPost('',
) and that would mean norefresh
on the client side. But you can still do something at the completion of the operation by using the onComplete
property as you can see from the example.
In the params
property goes an additional object with a property that defines the id of the action you want to fire: { '$$xspsubmitid' : '#{id:eventAutoSave}' }
.
In conclusion, every 5 seconds, the above code will:
No additional dependencies or snippets. It works just like that out of the box.