Giter Club home page Giter Club logo

angular-unsavedchanges's Introduction

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 $locationChangeStart and $stateChangeStart
  • Can be configured to listen for any event

How it Works

The directive binds to locationChangeStart and window.onbeforeunload. When these events happen all registered froms are checked if they are dirty. The module defers to the forms $dirty property as a single source of truth. If dirty, the user is alerted. Disregarding changes resets the form and sets pristine.

Basic Usage

  • Install from bower using $ bower install angular-unsavedChanges --save.
  • Include the JS, for example <script src="bower_components/angular-unsavedChanges/dist/unsavedChanges.js"></script>.
  • Include in your app, for example: angular.module('app', ['unsavedChanges', 'anotherDirective'])
  • Add attribute to your form, unsaved-warning-form
  • That's it!

API

Directives

The module provides three directives for use.

unsaved-warning-form

Add 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-clear

Add to button or link that will disregard changes, preventing the messaging when user tries to navigate. Note that button type should be reset.

<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>

resettable

Add to inputs that use ng-model to reset model values when user dismisses changes or clicks the unsaved-warning-clear button.

<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 Configuration

A number of options can be configured. The module uses the Object.defineProperty pattern. This avoids the need for custom getters and setters and allows us to treat configuration as pure JS objects.

useTranslateService

Defaults to 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;

logEnabled

Defaults to false. Uses the services internal logging method for debugging.

unsavedWarningsConfigProvider.logEnabled = true;

routeEvent

Defaults to ['$locationChangeStart' ,'$stateChangeStart'] which supports ui router by default.

unsavedWarningsConfigProvider.routeEvent = '$stateChangeStart';

navigateMessage

Set custom message displayed when user navigates. If using translate this will be the key to translate.

unsavedWarningsConfigProvider.navigateMessage = "Custom Navigate Message";

reloadMessage

Set 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 Bugs

*** Known issue: sometimes the form is removed from expected scope. Ie: in your controller $scope.formName no longer works. You might need to access $scope.$$childTail.formName. This will be fixed in furture versions.

Demo / Dev

To try the demo run npm install && bower install && grunt connect. The browser should open http://127.0.0.1:9001/demo.

Test

Note you need to manually change the paths in index.html and karam-unit.conf to point to the dist version for final testing. Make sure to run $ grunt first.

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.)
  • Run $ grunt test:e2e

Unit Tests

  • Run $ grunt test:unit OR $ grunt test

Build

Run $ grunt to lint and minify the code. Also strips console logs.

License

The 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.

angular-unsavedchanges's People

Contributors

brettshollenberger avatar dmytroyarmak avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

angular-unsavedchanges's Issues

Project status - abandoned?

Hi @facultymatt - looks like some great work here but I'm hesitant to use in a build as it looks abandoned based on open issues/PRs that haven't been responded to.

Is there any intention to pick it back up?

not working with ng-form

The todo "refactor, temp fix for issue #22" causes an issue where ng-form is not respected.

I'm fixing it on my fork by doing something like:

   while(count < 3 
        && formElement[0].tagName !== 'FORM' 
        && !_.findWhere(_.values(formElement[0].attributes), {name:'ng-form'})) 

Sorry about the underscore but I've already had to fork to add support for custom dialogs and I brought in a ui-router on my fork as a dependency, but if this helps then thats great.

support custom alert strategies

If the user doesn't want to use window.confirm, they should be able to provide a custom strategy.

The strategy should be return a deferred promise. It will resolve / reject based on the user confirming or rejecting the alert.

Issue with 'resettable' attribute/directive

Thanks for this implementation - very useful. However, the 'resettable' attribute can cause issues. It seems fine with a simple ng-model binding, but if there is some function called on ng-change, the value of the object will not have changed. For example I have:

<select id="owner" 
    class="form-control" 
    ng-model='owner' 
    resettable 
    required 
    ng-change="selectOwner()"  
    ng-options="user.friendlyName for user in owners" />

Now if after selecting some new value I examine the object $scope.owner inside the ng-change function it will not be the new value but the old one. I've not been able to work out yet from the directive code why this should be so.

[enhancement] Add missing bower.json.

Hey, maintainer(s) of facultymatt/angular-unsavedChanges!

We at VersionEye are working hard to keep up the quality of the bower's registry.

We just finished our initial analysis of the quality of the Bower.io registry:

7530 - registered packages, 224 of them doesnt exists anymore;

We analysed 7306 existing packages and 1070 of them don't have bower.json on the master branch ( that's where a Bower client pulls a data ).

Sadly, your library facultymatt/angular-unsavedChanges is one of them.

Can you spare 15 minutes to help us to make Bower better?

Just add a new file bower.json and change attributes.

{
  "name": "facultymatt/angular-unsavedChanges",
  "version": "1.0.0",
  "main": "path/to/main.css",
  "description": "please add it",
  "license": "Eclipse",
  "ignore": [
    ".jshintrc",
    "**/*.txt"
  ],
  "dependencies": {
    "<dependency_name>": "<semantic_version>",
    "<dependency_name>": "<Local_folder>",
    "<dependency_name>": "<package>"
  },
  "devDependencies": {
    "<test-framework-name>": "<version>"
  }
}

Read more about bower.json on the official spefication and nodejs semver library has great examples of proper versioning.

NB! Please validate your bower.json with jsonlint before commiting your updates.

Thank you!

Timo,
twitter: @versioneye
email: [email protected]
VersionEye - no more legacy software!

Ipad bug

When I try to use this into an Ipad device (safari) the message never appear.

Detect confirm

Hello everyone, I 've been using angular-unsavedChanges and everything seems to work fine.
However, I woud like to try to detect whenever the user confirms the alert box prompt by the directive.
What is the cleanest way to do that?

Regards,
Billy

Form not set to pristine after unsaved changes were disregarded

Hello,

First of all thank you for this directive, it's very helpful and easy to use!

The documentation describes the following behaviour:

The module defers to the forms $dirty property as a single source of truth. If dirty, the user is alerted. Disregarding changes resets the form and sets pristine.

I don't see that behaviour happening, and I don't see it in the code either: the form doesn't get set to pristine after the user decided to disregard the unsaved changes.

As a result of this, I get 2 confirmation dialogs. My routeEvent configuration is as follows:

unsavedWarningsConfigProvider.routeEvent = ['$locationChangeStart' ,'$stateChangeStart', 'accordionPanelChangedEvent', 'dossierClosedEvent'];

When I broadcast the dossierClosedEvent, the confirmation dialog is shown. When I decide to ignore the changes, my page redirects, so one of the location-change-events is caught and the confirmation box shows up a second time because the form is still dirty. Will this behaviour be changed in a future version or can I work around it somehow? Currently the behaviour doesn't match with the pasted documentation.

Add support for usage within Modal Dialogs

I am using the UI Booststrap Modals.

Currently if multiple forms exist but some of them are contained within Modal Dialogs there are the following problems:

  • The modal exit/close/dismiss events are not detectable by the unsavedChanges framework.
  • If a change has occured in a layer underneath, all forms in modal dialogs above will complain about unsaved changes.

One solution could be to add an additional directive ( or a priority attribute). Instead of using a flat array of forms inside Unsaved Changes, it could be possible to have a priority queue of forms where the following applies:

-if all forms have same priority all forms are checked.
-Otherwise, get forms of same highest priority and only check those.
-Once the appropriate save/dismiss etc actions are taken, remove the forms from the queue/data structure so they are not further checked.

This is just an idea for a possible solution.

Finally, within my own code I am currently using the following in the cancel function which is a bit annoying:

    @cancel = ->
        checkUnwantedNavigation = (unsavedWarningSharedService.confirmExit())   # will return undefined/null if form is not dirty
        if (!$scope.assessForm.$dirty || confirm(checkUnwantedNavigation))      #this is annoying, should be able to specify priority in the directive
            $modalInstance.dismiss()

Console error with IE 9

Getting 'console' is undefined-type: exception with IE 9, it only happens when you open the form for first time, without a console.

ReferenceError: 'console' is undefined-type: exception

Confirm Box is Displaying [Object,Object]

I'm not sure whether I am missing a configuration or whether something has changed in browsers. Definitely the onUnload behavior has changed in firefox for example: http://stackoverflow.com/a/9420213/1688441

Instead of the messages I am getting [ object, object ] .

I have pulled a local copy of the code and changed the confirmation line to:

  if (!confirm(messages.navigate.$$state.value))

instead of

  if (!confirm(messages.navigate))

Unsaved dialog appears when I disable the submit button

After I filled all my form and pressed on submit button the Unsaved dialog appears. My submit button called a function and in that function I have an ajax request and in the success part I make a redirect to another location.

This doesn't happen if I don't disable the button.

I am using Angular version 1.2.27

Cancel save of form within tab

We have our forms setup within tabs. If the user changes data and tries to navigate to another tab without saving the confirm dialog appears correctly. When the user chooses cancel though the tab they selected appears as the selected tab. How can I detect that they clicked cancel and reset the selected tab?

Issue with Isolate scope directive disallowing the use of other directives on form element.

In 0.2+ there was introduced the use of isolate scope in unsavedChangesForm directive. This makes impossible for other directives that create child scopes to work in the same element as the form directive.
Do you actually need to create an scope at all for the directive, seems rather unnecessary. If you do require to isolate the scope could you at least make the directive require the form controller at parent level as well as element ( as in require: '^form') so we can move the unsavedChangesForm directive to a stand alone element inside a form.

Replacement for confirm

Hello, i had discovered angular-unsavedChanges project throw Googling, and so far everything looking good and promising. So far i got 2 questions:

  1. Is there any specific reason why this project not published on NPMJS ? (except of capital letter in name) ?

  2. If you would be required to replace windows.confirm with alternative dialog pop-up as one from angular UI-Bootstrap modal pop-up http://angular-ui.github.io/bootstrap/#/modal , how would you do so ?

Thank you in advance ...

Only one resettable being reset?

I found only one of my resetables was being reset. I adjusted the code to this:

var resettables = formElement[0].querySelectorAll('[resettable],[data-resettable]');
for (var i = 0; i < resettables.length;i++) {
    scope.$apply(angular.element(resettables[i]).triggerHandler('resetResettables'));    
}

It seems to work now. I didn't spend much time so let me know if I'm missing something and this is not actually needed.

Warning alert shows up twice

The alert dialog box appears again after clicking ok to say you're okay with leaving these changes behind. Here's what I'm using on the form tag, I'm not sure why it shows up twice

Anyone using angular-gettext?

Hello;

I am using angular-gettext for translation.

I need to inject gettext service to configuration but it didn't work.

MetronicApp.config(function(unsavedWarningsConfigProvider){
unsavedWarningsConfigProvider.navigateMessage = gettext("Navigate message");
unsavedWarningsConfigProvider.reloadMessage = gettext("You will lose unsaved changes if you reload this page!");
});

What should I do to translate alert messages?

Clarification in documentation about unsaved-warning-clear

Clarification on unsaved-warning-clear is needed. It states to use unsaved-warning-clear on a button or link that will disregard changes, but could unsaved-warning-clear be used to denote fields that should not trigger the form to be dirty?

Looking to disable the directive based on condition

Hi, thank you for this great directive, I would like to disable/enable the directive based on a condition, is that possible with the existing code? if not, would it be a major change?

I'm thinking on something like this:

or

Angular doesn't have a way to dynamically add/remove directives, that would require re-compiling, so my approach would be to always add the directive but enable/disable it's functionality based on a condition/boolean.

Thanks!

Ability to ignore a non-reset button (i.e. non-submit type Submit)

Matt,

Great plugin. Implementing in a project and having issues with the warning being triggered when submitting a form. My current submit button is not type="submit", and was hoping you might have a suggestion on how to ignore warning and allow the form to submit in this case. My button code is below:

<button class="btn btn-primary" ng-click="confirm()">Save</button>

Thanks!

Reset / clear changes clears across all forms, should clear that form only.

To recreate:

  1. view the demo
  2. enter text into form 1 and 3
  3. press "disregard changes" on form 1.

What happens:

  1. Form 1 and 3 clear.

What should happen:

  1. Form 1 only should clear.

Why?

This has something to do with the scopes and isolate scopes created by the directives. Ideally we'd have something like this:

controller1:scope
|
|-- form1:isolateScope
|   |
|   |
|   |-- input1
|   |-- resetButton // broadcasts from form1:isolateScope
|
|-- form2:isolateScope
    |
    |-- input1
    |-- resetButton // broadcasts from form2:isolateScope

It may be because we broadcast to scope, and scope is the before compile scope??? and then we need to be broadcasting to the after compile scope?

Question: Known Bugs

I don't understand the issue in paragraph "Known Bugs". In which condition the removement of the form in the scope will be happen?

Does anyone has a workaround inside the module?

Kind Regards

How can I use it for RequireJS

Hi Matt,

I have used your library in AngularJS with little of modification mainly for ThridParty controls it is working absolutely fine.

Now we have requirements to implement with RequireJS with AngularAMD and we are loading Angular from AMD modules, now how we can implement UnsavedChanges library there.

When I am loading it from path/shim in main.JS with Angular as dependency, your library is not getting load.

Below is code I have in my Main.JS, and just to keep strait, I kept unsavedChanges.js file in same location where Main.JS file is.

require.config({

    baseUrl: "js/",

    // alias libraries paths
    paths: {
        'jquery': '../bower_components/jquery/jquery.min',
        'angular': '../bower_components/angular/angular',
        'angularAMD': '../bower_components/angularAMD/angularAMD',
        'bootstrap': '../bower_components/bootstrapjs/js/bootstrap.min',
        'unsavedChanges': 'unsavedChanges',
    },

    // Add angular modules that does not support AMD out of the box, put it in a shim
    shim: {
        'angular': ['jquery'],
        'angularAMD': ['angular'],
        'angular-ui-router': ['angular'],
        'bootstrap':
        {
            deps: ['jquery']
        },
        'unsavedChanges': {
            deps: ['angular'],
            exports: 'unsavedChanges'

        }
    },

    // kick start application
    deps: ['app']
});

Please let me know what I missing, resolving this your library may help lot more people who are implementing AMD and who knows may be for TypeScript :)

Thanks
Rushi

bind to multiple events

Listener event should accept array and in that case bind to each event in array. Needs to unbind also.

todo:

  • Update routeEvent to accept array and listen for multiple events
  • Unbind these events properly
  • Update spec and unit tests

Are there any examples for using this with Angular Translate?

It seems useTranslateService is disabled in the demo.

I'm having trouble getting it working, so I just wanted to see a working example.

I've added the strings to my translations but I keep seeing [object Object]

        $translateProvider.translations('en', {
            LOADING: 'Loading',

            reloadMessage: 'Reload message',
            navigateMessage: 'Navigate message'
        });

Probably I've got a problem somewhere else though...

I'm using this as my translation engine: https://angular-translate.github.io/

Exclude Warning Messages to Specific Routes (Server route)

I have integrated this library to my app, it's working fine but I wanted to exclude 'unsaved warning popup' to 'Log Out (server route change)' function call because I don't want to show 'unsaved warning window' up on this call.

Is there any way to do that with in the library?

Confirm dialog appears twice

When using Angular UI Router, the angular-unsavedChanges module display the confirm box twice, because two events are being watched: $locationChangeStart and $stateChangeStart.

screenshot from 2014-11-19 21 28 36

Did I'm missing something or is it a bug?

bower.json incorrect main path

The main property in your bower.json incorrectly refers to unsavedChanges.js rather than dist/unsavedChanges.js. This breaks tools like wiredep. Can you please update your main path to reflect the correct path to the source?

<a><button></a> broken

Hi Matt,

thanks for the nice directive, generally it's working well.

I have an issue though where buttons in links inside forms are disabled. Not a big deal I can just use text links.

So :
<span ng-hide="user.has_file"> <a href="/cv_upload"> <button class="btn">Upload Your CV</button></span></a>

fails to fire on the button press but :
<span ng-hide="user.has_file"> <a href="/cv_upload">Upload Your CV</span></a>
works fine.

How to apply the directive AFTER data is loaded through HTTP?

Any time I add values to a form (that are loaded through HTTP), this library always thinks my form is dirty. Any easy workaround? I want the dirty check to start taking place only after a form is initialized.

Thanks a ton for your quick guidance.

bower.json small bug

You have small bug in bower.json.

You need to change it from:

"main": "unsavedChanges.js",

To

"main": "dist/unsavedChanges.js",

ngTable Coloumn Customization

Dear Team,

As i have an One table it was some customized Filters and Sorting, but i want to make some column wise customization like

In top of the table i have an One Button, if i click the Button it will shows some one Multi Select Box with Drag Drop option.
In Side of Lpanel I have an options are table header title
Cpanel is select a coloumn title selective options like Lpanel to rpanel, Rpanel to Lpanel, All to Rpanel, All to lapnel insid ehave Drag Drop.
In table header name(data) are loaded into the Select Box. whatever i action will perform like L to R R to L, Drag and Drop
I want to hide the columns and i want make the column positions too

Kindly Help me for an custom Directive / Controller,

Hazdik DA, India

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.