How to protect a form from accidentally losing data
Posted by Jim Morris on Tue Nov 14 22:47:44 -0800 2006
Here is a good one, your users are complaining that they have spent 5 minutes filling in a web form, then lose it all by clicking on a link away from the form, and back arrow doesn't restore the fields that were entered. (At least on IE).
I saw an interesting design in the Rails Recipes book for saving form data at regular intervals, but I didn't like the overhead of that one.
So I found a snippet of javascript that detects when you are about to switch away from a page, I coupled that with a simple observer that sets a dirty flag when any field is modified in anyway, and you get something that pops up a dialog asking if they really want to leave this page and lose their input data, they get the option to not go to the new page and submit the form instead.
This is not as clean as I'd like, so anyone with ideas of making it cleaner please let me know.
This would go in app/helpers/application.rb...
END end ]]>In the view you give the form an id, and you also need to add an :onclick to the submit button so it allows you to submit the form without popping up the dialog...
<% form_for :obj, @obj, :html => {:id => 'new_form'}, :url => { :action => 'create' } do |f| %>
...
<%= submit_tag "Submit", :onclick => "dirty_page= false;" %>
then call this somewhere in that view...
catch_page_change_if_changed('new_form', @obj)
Basically I just set a javascript variable called dirty_page to false, and have the observer set it to true if anything changes. The optional object being passed in to the helper is used to detect if we are back on the form due to input errors, in which case we set the dirty_flag to true, so we don't lose the fields that were not in error.
Oh this only works if javascript is enabled (of course!)
This is great, but it DOES NOT WORK AS POSTED!!!
You need to encapsulate new Form.EventObserver within an onload observer, AND they way you are refering to the form #{form} needs to be in quotes or the form is never found.
Like this
def catch_page_change_if_changed(form, obj= nil)
<<-END
<script language="javascript">
<!--
dirty_page = #{obj.nil? || obj.errors.empty? ? "false" : "true"};
new Event.observe(window, 'load', function() {
new Form.EventObserver($("#{form}"), function(element, value) {dirty_page= true; });
});
window.onbeforeunload = function (evt) {
if (dirty_page) {
var message = 'Form values have not been submitted, leaving without submitting will loose all inputs.';
if (typeof evt == 'undefined') {
evt = window.event;
}
if (evt) {
evt.returnValue = message;
}
return message;
}
}
//-->
</script>
END
end
its scrubbing my inputs, it needs this change in the application helper:<BR><BR>
new Event.observe(window, 'load', function() {<BR>
new Form.EventObserver($("#{form}"), function(element, value) {dirty_page= true; });<BR>
});