An AngularJS directive for forms that alerts user of unsaved changes.Dev Note: This module is still in development. However it's used in many of my production projects so it can be considered stable and battle tested.
This directive will alert users when they navigate away from a page where a form has unsaved changes. It will be triggered in all situations where form data would be lost:
- when user clicks a link
- when user navigates with forward / back button
- when user swipes (iOS)
- when user refreshes the page
In addition this module:
- Works with multiple forms on the same page
- Provides a button to disregard unsaved changes
- Works with Angular Translate module
- Has configurable reload and navigate messages
- Works with uiRouter by default by listeneing for
- Can be configured to listen for any event
How it WorksThe directive binds to
window.onbeforeunload. When these events happen all registered froms are checked if they are dirty. The module defers to the forms
$dirtyproperty as a single source of truth. If dirty, the user is alerted. Disregarding changes resets the form and sets pristine.
- Install from bower using
$ bower install angular-unsavedChanges --save.
- Include the JS, for example
- Include in your app, for example:
angular.module('app', ['unsavedChanges', 'anotherDirective'])
- Add attribute to your form,
- That's it!
DirectivesThe module provides three directives for use.
unsaved-warning-formAdd to forms you want to register with directive. The module will only listen when forms are registered.
<form name="testForm" unsaved-warning-form> </form>
Optionally, you can add to an element within a form:
<form name="testForm"> <div unsaved-warning-form> </div> </form>
When used in this way, it must be no more then 3 levels nested within parent form.
unsaved-warning-clearAdd to button or link that will disregard changes, preventing the messaging when user tries to navigate. Note that button type should be
<form name="testForm" unsaved-warning-form> <input name="test" type="text" ng-model="test"/> <button type="submit"></button> <button type="reset" unsaved-warning-clear></button> </form>
resettableAdd to inputs that use
ng-modelto reset model values when user dismisses changes or clicks the
<input name="email" ng-model="email" resettable />
Note that if you have multiple forms on the page, only the model values inside the form which was reset will be effected.
On page change or reload, all model values will be effected.
Provider ConfigurationA number of options can be configured. The module uses the
Object.definePropertypattern. This avoids the need for custom getters and setters and allows us to treat configuration as pure JS objects.
true. Will use translate service if available. It's safe to leave this set to
true, even when not using the translate service, because the module still checks that the service exists.
unsavedWarningsConfigProvider.useTranslateService = true;
false. Uses the services internal logging method for debugging.
unsavedWarningsConfigProvider.logEnabled = true;
['$locationChangeStart' ,'$stateChangeStart']which supports ui router by default.
unsavedWarningsConfigProvider.routeEvent = '$stateChangeStart';
navigateMessageSet custom message displayed when user navigates. If using translate this will be the key to translate.
unsavedWarningsConfigProvider.navigateMessage = "Custom Navigate Message";
reloadMessageSet custom message displayed when user refreshes the page. If using translate this will be the key to translate.
unsavedWarningsConfigProvider.reloadMessage = "Custom Reload Message";
Gotchas / Known BugsKnown issue: sometimes the form is removed from expected scope. Ie: in your controller
$scope.formNameno longer works. You might need to access
$scope.$$childTail.formName. This will be fixed in furture versions.
Demo / DevTo try the demo run
grunt connect. The browser should open http://127.0.0.1:9001/demo.
TestNote you need to manually change the paths in
karam-unit.confto point to the
distversion for final testing. Make sure to run
End 2 End Testing Because of the alert / event driven nature of this module it made the most sense to rely on e2e tests. (also its hard to interact with alerts via unit tests).
To run the e2e tests do the following:
- Install Protractor as per directions here: https://github.com/angular/protractor
- Start selenium server:
webdriver-manager start(or use other selenium methods as per Protractor documentation.)
$ grunt test:e2e
$ grunt test:unitOR
$ grunt test
$ gruntto lint and minify the code. Also strips console logs.
LicenseThe MIT License (MIT)
Copyright (c) 2013-2014 Matt Miller
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.