Tags
I have recently had to implement a new requirement for all pages in our app that have a RichFaces Tab Panel. This post is about RichFaces 4.2.Final.
Requirement
Change tabs with Unsaved Changes:
1. Issue the following warning:
There are pending changes to the current tab that have not been saved. Do you wish to proceed with the tab change, or cancel and return to the tab?
2. The User may elect to either:
2.1. Proceed; The changes to the current tab are discarded.
2.2. Cancel; The use case continues at the step at which the user elected to change tabs.
Analysis
rich:tabPanel provides us with the following tools:
- We can call a server method when the tab is changed (if we use ajax), but w/o ability to stop the tab change or show any warnings
- We can call arbitrary javascript with ability to warn the user and abort tab switch, but the javascript is rendered at page render time, not at tab switch time, so no info from the server can be communicated
I did not want to get into server side tracking of all changes, since that would really complicate all pages, so the solution must be client side only.
Solution
I have created a very simple framework that I call Dirty Field Handler. It works as follows:
- dirtyFieldHandler.js attaches a “delegated” event handler to the document, which will handle all “change” events from any input field on the page
- Developer can use the DirtyFieldHandler framework as follows:
<!-- Include the framework on the page that needs it --> <h:outputScript library="javascript" name="dirtyFieldHandler.js" target="head"/> ..... <rich:tabPanel switchType="ajax" <!-- Can't call the server w/o using ajax tab switches! --> onbeforeitemchange='return DirtyFieldHandler.warnUser("#{msgs["common.warn.unsavedTab"]}");' <!-- Smart Javascript popup --> itemChangeListener="#{initiativeBackingBean.tabSwitchAction}"> <!-- Notify the server that tab was switched --> ...... </rich:tabPanel>
Code
dirtyFieldHandler.js:
/** * Provides the ability to detect if any input fields on the page were dirtied. * * Exposes DirtyFieldHandler.warnUser(message, [true/false]) function that can be called * by the page to warn the user about dirty fields. * * @author Val Blant */ if (!window.DirtyFieldHandler) { window.DirtyFieldHandler = {}; } (function($, dfh) { dfh.setDirty = function() { dfh.dirty = true; } dfh.resetDirty = function() { dfh.dirty = false; } /** * Warns the user if any fields are dirty. If the user chooses * to navigate away, clears the dirty flag, unless the 2nd parameter is used * to override that behavior */ dfh.warnUser = function(event, message, clearDirty) { var leavePage = true; clearDirty = typeof clearDirty !== 'undefined' ? clearDirty : true; if (dfh.dirty) { leavePage = confirm(message); if ( leavePage && clearDirty) { dfh.resetDirty(); } } return leavePage; } // Init dirty flag // dfh.resetDirty(); // Attach a "delegated" event handler to the document. All events that bubble // from input elements under this document will execute the handler. // We use a delegated handler b/c this approach will detect input field // changed or added by ajax requests // $(document).on("change", ":input", dfh.setDirty) }(jQuery, window.DirtyFieldHandler));