23 October, 2012

Warning a user that the form has changed - Tabular Form Style.


Quite some time ago on our web site, I wrote a quit tip about How to Warn a User That the Form Has Changed. The gist of the post was this:
"It's a common problem. A user spends time entering data into a form and then, for some reason, clicks a button or tab that will navigate away from the form without saving his data. Wouldn't it be nice if there were a way to warn the user that the data hasn't been saved, and that they may lose their work? "
The original solution used JavaScript to do the following:

create and set a flag at the page level
register an onchange event against all of the fields in the APEX form that sets the flag any time a value is changed
register with the page onbeforeunload event to check to see if the flag had been set and pop-up a warning if it has been

The original script worked well for a standard form but I had never tested it agains a tabular form.  Well, someone else had, and let me know that it really didn’t work.

As you can imagine, there are a lot of moving parts in a tabular form, especially now with APEX 4. The original script would have registered onchange events against every item on the form whether it was user editable or not.  That obviously would’t work for a tabular form as there are some JavaScript events that fire and change some of the hidden items.

After thinking about this for bit, I finally realized that instead of registering onchange events against every item on the tabular form, only the items that are user editable should be registered. If you look at the editable items, the id of each will conform to the following format:  f99_9999

So the key was to register only those items who’s id conforms to that format.  To do that the JavaScript match function and regular expressions can be used for this purpose.

The following JavaScript is the key. You can include it either in the HTML HEADER of the page, or in an HTML Region on the page:

<script type="text/javascript">

function onChangeinit() {

// This function sets up those fields which should
// trigger the "Are You Sure" Popup box upon navigating
// away from the page.

// First set up the array of elements in the form
// This method uses JavaScript to create an array of elements.
 var fields = document.getElementById('wwvFlowForm').elements;

// Now loop through the array and see if the id matches the format f99_9999
// If it does,  assign the on-change event.
//
// The onchange function sets the value of a JavaScript variable
// to '1' to show that something has changed.
//

 for (var i=0; i<fields.length; i++)

   if (fields[i].id.match('^f[0-9]{2}_{1}[0-9]{4}$'))
   {
     $x(fields[i]).onchange = function () {window.unsaved=1;}
   }
}

// Now Set up the UNSAVED variable
// and the function that checks it on UNLOAD.
window.unsaved = '';
window.onbeforeunload = function() {
return window.unsaved ? 'There may be unsaved changes to your data.' : undefined;
 }

// And you're done.
</script>

The onChangeinit JavaScript function needs to be run whenever the page loads. To do that, we call it by placing the following JavaScript in the Execute when Page Loads attribute at the page level.

onChangeinit();"

Finally, we want to provide a way to short circuit this for instances where we want them to be able to press a button without getting the message. The most common example of this would be the SAVE button. Again, you can include it either in the HTML Header of the page, or in an HTML Region on the page.

<script type="text/javascript">

function preSubmit() {

// Call this before any action where you want the user
// to be able to navigate without the warning message.

   window.unsaved='';
}
</script>

The last thing to do is edit any buttons that we want to be able to submit the page without being warned and make sure they call the preSubmit JavaScript function.

To make this change to SUBMIT buttons, edit the button and do the following:

In the Action When Button Pressed region, change the Action to Redirect to URL
in the URL Target enter the following:  javascript:preSubmit();apex.submit('SUBMIT');

Make sure the that ‘SUBMIT’ in the code above is actually the name of the button that you’re editing. That way when the apex.submit JavaScript function is called, it will be as if the user pressed the button.

For the DELETE buttons, edit the button and do the following:

In the Action When Button Pressed region, change the Action to Redirect to URL
in the URL Target enter the following:  javascript:preSubmit();apex.confirm(htmldb_delete_message,'MULTI_ROW_DELETE');

Again, make sure the that ‘MULTI_ROW_DELETE’ in the code above is actually the name of the button that you’re editing.

Once all of these things are put together, this should work nicely with tabular forms.

Hope this help some of you out, and as always comments are encouraged.

6 comments:

Bill said...

After trying many other methods of removing the javascript popup window, this one finally did it, EXCEPT it seems to have removed he warning completely. Making a change to the form and clicking on any different link will discard the changed data and switch to the page without a warning. The submit, delete, and add buttons do work as intended though. Do you know of any way to get the warning back for everything that isn't one of the buttons?

Steve Maxwell said...

Great article Doug. I found your method easy to understand and implement. One small thing that I noticed is that if you add a new tabular form row, it does not detect changes in that row. The issue was easy to overcome though by simply calling a function to set window.unsaved to 1 before the addRow() function.

Anonymous said...

I just googled across this 2-year old post, and it was exactly what I needed. Thank you!

Anonymous said...

Hi,
This is a wonderful post and exactly what I want except for one issue.
I have a button which doesnot submit the page but instead it opens up a popup window(another page).But when I update a record and click on the button without saving ,message is not displayed.
What can be done for the same?

Thanks.

Doug Gault said...

Anonymous,

If I understand you correctly, you want to warn the user before the pop-up is shown. To do that you'll have to inject some JavaScript that checks the value of the window.unsaved variable to see if it's '1' before you pop open the window.

Hope this helps.

Doug Gault said...

To answer Bill's question.

The way the example is executed (Using the window.onbeforeunload event), it should catch any navigation away from the page that doesn't call the preSubmit() function.

I don't know why this might not be happening in your case. If you can reproduce this on apex.oracle.com, I'd be happy to look at it.