Git Product home page Git Product logo

angular-input-modified's Introduction

Angular Input Modified

angular-input-modified

Bower version npm version

This Angular module adds additional properties and methods to the ngModel and ngForm controllers, as well as CSS classes to the underlying form elements to provide end-user with facilities to detect and indicate changes in form data.

This extra functionality allows you to provide better usability with forms. For example, you can add decorations to the form elements that are actually changed. That way, user will see what values has changed since last edit.

Also, you can reset an entire form or just a single field to it's initial state (cancel all user edits) with just a single call to the reset() method or lock new values (preserve new state) just by calling overloaded $setPristine() method.

Demos and examples

Please see the demos hosted on our GitHub Pages or open them locally.

Also, feel free to play with our Plunk!

Decorations and animation

This module adds ng-modified and ng-not-modified CSS classes (names are customizable) to the input fields to indicate their state. Use them in your CSS to decorate input fields. You can combine multiple classes in the same selector. For example, use this convenient CSS selector to decorate modified elements as valid:

/** Decorating only modified inputs as valid */
input.ng-valid.ng-modified {
    border-color: green;
}

This way end user will see what elements were actually changed.

This module also supports animations if ngAnimate module is available.

Installation

Install library with yarn

yarn add -D angular-input-modified

Install library with npm

npm i -D angular-input-modified

Add library to your page

<script src="/node_modules/angular-input-modified/dist/angular-input-modified.js"></script>

You should use minified version (angular-input-modified.min.js) in production.

Add dependency in your application's module definition

var application = angular.module('application', [
  // ...
  'ngInputModified'
]);

Usage

Please see our demos and examples as well as API documentation.

Form initialization

Starting from version 2.0.0 form must be synchronously initialized during controller execution. If you need some data to be fetched prior to form initialization — the best approach is to resolve this data using your router.

However, if you really need to re-initialize form after controller execution — please use the approach shown in this demo: Delayed Initialization.

Excluding some fields

Input modified module provides you with the ability to control which input elements will exhibit modifiable behavior and which will not.

By default all form fields in your application will support modifiable behavior, after input modified module is added to the application. You can control this via enableGlobally() and disableGlobally() methods of the inputModifiedConfigProvider. This gives you the overall top-level switch to control modifiable behavior.

Also, we provide you with special directive called bsModifiable that allows you to control which fields will support the behavior. It gives you are more granular control over your forms. This directive works in a recursive manner and can be applied to any HTML element. For example, you can apply it to an entire form: <form name="myForm" bs-modifiable="true"> in order to enable modifiable behavior on all it's fields.

bs-modifiable attribute can be set to true or to false, depending on what you are trying to achieve.

You can exercise the exclusion policy by excluding only specific fields or you can exercise the inclusion policy by disabling the behavior globally and then adding modifiable behavior only to the required forms or form fields. It's all up to you!

Please see the special demo.

Listening for form changes

When a form is modified, it fires the inputModified.formChanged event. Parent scopes can listen to this event. Modification flag and reference to the form controller are passed to event listener. Following is an example of parent scope listening to this event.

/**
 * @param {object}  event
 * @param {boolean} modified
 * @param {object}  formCtrl
 */
$scope.$on('inputModified.formChanged', function (event, modified, formCtrl) {
  // Process the modified event,
  // use formCtrl.$name to get the form name.
});

API

inputModifiedConfigProvider

Use this provider to configure behavior of this module. Every setter of this provider supports methods chaining. See example:

angular.module('Application', ['ngInputModified'])
  .config(function(inputModifiedConfigProvider) {
    inputModifiedConfigProvider
      .disableGlobally()
      .setModifiedClassName('my-changed')
      .setNotModifiedClassName('my-clear')
    ;
  })
;
Method Description
enableGlobally() Enables modifiable behavior globally for all form elements (this is default)
disableGlobally() Disables modifiable behavior globally for all form elements
setModifiedClassName({string} className) Provides CSS class name that will be added to modified elements. ng-modified is the default one
setNotModifiedClassName({string} className) Provides CSS class name that will be added to unmodified elements. ng-not-modified is the default one

ngModel

Property Type Description
masterValue mixed Initial value of the form field
modified boolean Flag that indicates whether the form field was modified
Method Description
reset() Resets input value to it's initial state
$setPristine() Makes form field pristine by preserving current value as a new master value

ngForm

Property Type Description
modified boolean Flag that indicates whether the form is modified
modifiedCount integer The number of modified form fields
modifiedModels array The list of modified model controllers
modifiedChildFormsCount integer The number of modified child forms
modifiedChildForms array The list of modified child form controllers
Method Description
reset() Resets all input fields of the form to their initial states
$setPristine() Makes form pristine by making all child forms and form fields pristine
Event Listener Attributes Description
inputModified.formChanged event, modified, formCtrl Fired up through the scope chain when form is changed

bsModifiable

This directive can be applied to any element on the page. All descendant form fields (recursively) will respect it.

Attribute Type Description
bs-modifiable string Either "true" or "false", see "excluding some fields" chapter

Changelog

Please see the complete changelog for list of changes.

Feedback

If you have found a bug or have another issue with the library — please create an issue.

If you have a question regarding the library or it's integration with your project — consider asking a question at StackOverflow and sending me a link via E-Mail. I will be glad to help.

Have any ideas or propositions? Feel free to contact me by E-Mail.

Cheers!

FAQ

How do I access demos locally?

Node.js must be installed in your OS.

  • clone the repo
  • npm i && bower install to initialize the project
  • install Gulp by running npm i -g gulp
  • run gulp demo in the repo's root directory
  • open http://localhost:8888/

Developer guide

Fork, clone, create a feature branch, commit, create a PR.

Run:

  • yarn install && bower install to initialize the project
  • gulp build to re-build the dist files
  • gulp demo to run local webserver with demos on port 8888
  • gulp demo-deploy to deploy GitHub Pages

Do not add dist files to the PR itself. We will re-compile the module manually each time before releasing.

Support

If you like this library consider to add star on GitHub repository.

Thank you!

License

The MIT License (MIT)

Copyright (c) 2014 - 2017 Slava Fomin II, BETTER SOLUTIONS

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-input-modified's People

Contributors

gaintsev avatar zxyang 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

Watchers

 avatar  avatar  avatar

angular-input-modified's Issues

Compare Types

Be able to in the provider specify if the comparisson of the values needs to be strict (checking types) or only by value.
Ex:
No detect changes on 1 == '1'

"Ghost modified control"

We are using a system where parts of a form are shown or hidden with a ng-if "toggle".
Whenever i visit a certain part of a form, leave, and go back, this "ghost" modified control pops up.
to see this info, i added "{{form.resourcegeneralform.modifiedModels | json}}" to the html to see.

[ { "$viewValue": "", "$modelValue": "", "$validators": {}, "$asyncValidators": {}, "$parsers": [], "$formatters": [], "$viewChangeListeners": [], "$untouched": true, "$touched": false, "$pristine": true, "$dirty": false, "$valid": true, "$invalid": false, "$error": {}, "$name": "", "$options": { "allowInvalid": true, "updateOnDefault": true }, "modified": true, "masterValue": "[]" } ]

it has no name, the master value was an array but it went back to an empty string somehow. How can i find the offending control?

Console Error

This Error appears in the console at the start of my application.

bower-combined.js:5 TypeError: Cannot read property '$$onChildModelModifiedStateChanged' of null

image

Request: Option to not apply the plugin

Hi,

it would be very good if there was an option to not apply the plugin to a form. For instance, when using angular-input-modified together with ui-select, the following errors will commence:

Cannot read property '$setPristine' of undefined

So perhaps an option like no-input-modified, like this:

<form no-input-modified>...

Modified state based on $viewValue instead of $modelValue ?

Would it be possible to make ng-modified checks bsed on $viewValue instead of currently $modelValue ?

Use case: when using ng-pattern or any validation rules, ngModel is prevented to change while user actually modified the $viewValue.

Doesn't work on input file?

I was hoping this would work on a form I have that also has an input=file tag, but it does not seem to track that input. All other input types are working. Let me know if I am doing something incorrectly please.
Thanks

ngChange doesn't trigger? / getting data from modal window

Hi. i need to check if data from modal window form is changed.
if i set it up like this
" ng-model="variation_form.modified" ng-change="change()" value = "{{ variation_form.modified ? '1' : '0' }}">
then manually changing input value will trigger change function, but changing it via changing any form element it doesnt trigger. Any help? Could be any workaround . I have function ok() that is closing modalInstance and there i need to access modified value.

Doesn't work with Ui-Bootstrap's Timepicker

I was testing your module to use in my current project but was getting issues when using the Ui-Bootstrap timepicker. I have a Plunker here demonstrating the issue: http://plnkr.co/edit/yZPDr3RFIOWBvcQZiyGw?p=preview

From what I can tell your code does not create the modified flag and does not store the masterValue.
i.e. myForm.fieldName.masterValue === undefined returns true as does myForm.fieldName.modified.

What is interesting is that the input fields get the ng-modified class when they are not set to the original model value, but these other flags and values are not set.

Bug with angular 1.6.1

After upgrading from angular 1.5.10 to angular 1.6.1, i noticed this error:
TypeError: Cannot set property '$dirty' of null, because this line

// Calling overloaded method.
originalSetPristine.apply(this, arguments);

So, i changed the line above to this and everything is working fine

if (this) {originalSetPristine.apply(this, arguments);}

Programmatic reset

Hi,
Thanks for the repo. In my my use case I must call a function from a save button, ie ng-click="mySave()".which then sends form to my mongodb.If possilbe, how can I reset the field and or form from the function?
TIA
John

TypeError: Cannot read property '$$notifyModelModifiedStateChanged' of null

I used this module in a uib-modal (https://angular-ui.github.io/bootstrap/). The data modal that is passed to the modal is reloaded prior to being opened again.

I found that adding the same checks you had in onInputValueChanges() to setPristine() fixes it.

Changing from:
// Notifying the form.
formCtrl.$$notifyModelModifiedStateChanged(modelCtrl);

to:

                    // Notifying the form.
                    if (formCtrl && 'function' === typeof formCtrl.$$notifyModelModifiedStateChanged) {
                        formCtrl.$$notifyModelModifiedStateChanged(modelCtrl);
                    }

regarding the modal

Hello,
I need to know is there any way to use dirty check on modal instead of the form name element. If yes can you please write me the simple example.
And I have also one custom dropdown in my form.
Which is not there the name attributes on my html how can I track this.
I have past my html in the below mail.
Can you please see the below html and provide me the function in my JavaScript to check the dirty flag

Field Details

Field Name *

Farm Name *

{{acreage.toFixed(2)}} {{fieldUnit | uppercase}}
Current Season ({{seasonsName[0].substr(0,4)}} / {{seasonsName[0].substr(5)}})
Previous Season ({{seasonsName[1].substr(0,4)}} / {{seasonsName[1].substr(5)}})
Crop *
Planting Date *
Brand
Product
Product Details RTP-{{varietyDetailP01.rtp}} ; RTN-{{varietyDetailP01.rtn}} ; RTCC-{{varietyDetailP01.rtcc}}
Average Population
Brand
Product
Product Details RTP-{{varietyDetailP02.rtp}} ; RTN-{{varietyDetailP02.rtn}} ; RTCC-{{varietyDetailP02.rtcc}}
Average Population
Crop *
Planting Date *
Brand
Product
Product Details RTP-{{varietyDetailP11.rtp}} ; RTN-{{varietyDetailP11.rtn}} ; RTCC-{{varietyDetailP11.rtcc}}
Average Population
Observed Yield
Brand
Product
Product Details RTP-{{varietyDetailP12.rtp}} ; RTN-{{varietyDetailP12.rtn}} ; RTCC-{{varietyDetailP12.rtcc}}
Average Population
{{seasonsName[2].substr(0,4)}} / {{seasonsName[2].substr(5)}} Season
Planting Date *
Crop
Brand
Product
Average Population
Observed Yield
Brand
Product
Average Population
{{seasonsName[3].substr(0,4)}} / {{seasonsName[3].substr(5)}} Season
Planting Date *
Crop
Brand
Product
Average Population
Observed Yield
Brand
Product
Average Population
{{seasonsName[4].substr(0,4)}} / {{seasonsName[4].substr(5)}} Season
Planting Date *
Crop
Brand
Product
Average Population
Observed Yield
Brand
Product
Average Population
Save

Issues with ng-repeat

Unfortunately I have discovered another issue with this library. If you are using an ng-repeat it does not check if the entire form has been modified correctly.

I have an Plunk here as an example: http://plnkr.co/edit/b7OTGt6NTYIJDJIEHMe0?p=preview

If you change both fields and then change one back to the original value it thinks the entire form has not been modified. This is because you are adding models to your modified-models array based on the string used in the ng-model tag and not the actual object.

I also tried to use nested forms using the ng-form directive, however the top form does not see that its child forms have been modified.

Thanks for all the help.

When using select ng-modified isn't set to true until the second click.

I'm not sure if I had a special case, cause I was using ui-select (https://github.com/angular-ui/ui-select) in a bootstrap modal. The model is initially set to undefined and the array using for the options is only loaded when the modal is opened.
On opening the modal the modified value was set to false, which is fine, then when a value is selected in the select, it stays false, only when another value is selected was it set to true.

I was able to fix it by checking if the $modelValue was defined in the onInputValueChange function, like so:

              if ('undefined' === typeof ngModel.masterValue) {
                    // Initializing the master value.
                    ngModel.masterValue = ngModel.$modelValue;
                    // if the master value has a value, set modified to true
                    if (ngModel.masterValue){
                        ngModel.modified = true;
                    }
                    // Initially decorating the element.
                    toggleCssClasses();

                } else {
                     ...
                }

Add support for ngTagsInput

I'm trying to use the input-modified with the tags-list angular component that as far as I know is working with ng-model correctly.

I'v created this plunker to show the problem: https://plnkr.co/edit/bVbKfmE3hRID7Loq4E8N

I have no way of telling if the issue is on the input-modified side or the tags-list side.

Let me know what you think. The plunker has instructions on how to reproduce the problem.

[bug] Exception on resetting form when using nested using nested scopes

Here's a simplified version of the code that led me discover the bug:

<!-- html -->
<div ng-controller="MyCtrl as ctrl">
    <button ng-click="ctrl.resetForm()">Reset</button>
    <form name="myForm">
        <table>
            <tr ng-repeat="foo in ctrl.Foos">
                <td ng-repeat="bar in ctrl.Bars">
                    <input type="checkbox" ng-model="bar[foo.id]">
                </td>
            </tr>
        </table>
    </form>
</div>

// script
angular.module('app').controller('MyCtrl', function($scope) {
    ctrl.Foos = [ {id:'a'}, {id:'b'} ];
    ctrl.Bars = [ {a:true, b:false}, {a:false, b:true} ];

    this.resetForm = function ()
    {
        $scope.myForm.reset();
    }
})

On trying to reset the form from my controller, an exception gets thrown at ngModel.js:156 (and subsequently ignored in line 159). The error is

ReferenceError: foo is not defined

Any ideas for a bug fix?

Property `modified` on parentform not updated when childform was removed

My use-case is as follows:

I got a parentform with a dynamically generated list of childforms in it. The childforms are generated based on data fetched from a server.

Each time I receive new data from the server, I'd like to say that, since we have new data, the parentform should be made pristine. This is, after all, the new "truth" we have to work with right now: none of the childforms has been modified.

However, I did not find a proper way to reset the modified state of the parentform via angular-input-modified.

Example
I tried the following to tackle this issue:

parentform.html

<form name="parentform" bs-modifiable="true">
  <div ng-if="childforms.length" ng-repeat="childform in childforms" ng-form="{{ childform.id }}">
    <!-- contents of childform -->
  </div>
</form>

parentformDirective.js

...
link: function ($scope) {
  $scope.$watch('childforms', function () {
    $scope.parentform.$setPristine();
  });
  ...
}
...

This seems nice, but I ran into trouble in the following scenario:

  • Modify one of my childforms
  • angular-input-modified now marks my parentform as modified: true
  • A new set of childforms comes in from the server
  • The modified childform is missing from this set of childforms

Now, because of my $scope.parentform.$setPristine call, angular-input-modified does update the $pristine and $dirty variables of my parentform, but does not update the modified: true property on my parentform.

This is probably due to the fact that the modified childform is not in the new set of childforms and the onModifiedStateChange function does not update the appropriate properties on the parentform object (see https://github.com/betsol/angular-input-modified/blob/master/src/directive/form.js#L98).

Is there any other way to do this? If not, I think this is a bug in the onModifiedStateChanged function.

Consider renaming "modified" to "$modified"

This is only a suggestion. Since:

  • since we're modifying ng-model's native behaviour
  • not declaring a new directive
  • calling the module ngInputModified instead of something that's not in angular's namespace (like bsInputModified)
  • overriding the way $pristine is set to account for modified (again affecting angular's standard behaviour)

I propose we call our flag $modified. It would feel like a very fluid extension of angular's capabilities this way.

Let me know what you think!

Modified state gets screwed when a subform is toggled

Hi @slavafomin, that's a great library, really liking it.

Here's an issue I came across when working with "nested" forms. I've checked that it occurs on versions 2.3.2 and 2.4.2 (possibly others). I have a form with a nested ng-form, the former gets toggled with ng-if. It looks like I can trick it into a "modified" state.

Here's the Plunker: https://plnkr.co/edit/YuYuG6M3SGWYiFE3jBKa?p=preview.

  • Start with a form with selected checkbox "B":

modified_1

  • Select option "foo" in the sub-form. The form is modified as expected:

modified_2

  • Unselect checkbox "B". The form is still modified:

modified_3

  • Select option "B" again. The form values are now different from the starting ones, but it's no longer marked as modified:

modified_4

Please let me know if there's a workaround and/or fix. Appreciate your help!

How can I get this to work with angular-autofields?

Hey looks like an interesting library, but I'm struggling to get it to work with the angular-autofields library. I assume it's because that library dynamically builds and populates all of the input fields, but even after using the delayed initialization I'm struggling to get this to work. Any Ideas?

Consider decorating ngModel instead of overwriting it

I was just looking through the source and noticed that you are overwriting the ngModel directive with your own custom version. This can be very dangerous for people using different versions of Angular, and is not recommended.

Instead, consider refactoring your code to add a decorator to ngModel.

Not working with forms that are hidden (using ng-if)

Firstly, congratulations - this is a great library - Angular should do this by default!

I have some forms that need to be hidden using ng-if when the template loads. Unfortunately I guess the directive doesn't associate with the form when it gets included into the DOM and I get Cannot read property '$setPristine' of undefined when the data loads.

From the plunk, click "Show Form" then "Load Form"

http://plnkr.co/edit/NoKikD?p=preview

How do I know why the form is marked as modified?

I have a form that has some regular input fields and some drop-downs that use angular-ui-select. The modified state of regular inputs are easy to track via form.field.modified, but the drop-downs don't seem to have such an option.

Is there a way to inspect why the form gets set as modified? I'm trying to call form.$setPristine() in a number of places, but that doesn't help get the form cleared. Just need some debugging tools to understand what is going on.

Angular annotations.

You should annotate everything. Now, when using require.js and optimizing, mangled variables breaks the code regarding this module. I don't want to add angular-input-modified to ngAnnotate grunt task.

add license to bower.json file

can you add the license terms to the bower.json file so that sites like webjars.org can pick this component and pack it as a webjar?

TypeError: Cannot read property '$$notifyModelModifiedStateChanged' of null

Hi!

I have some legacy system and I got a problem with this midule. There is the form

<form novalidate name="studyDesignDetailForm" ng-init="initializeForm()" class="form-horizontal"
                  role="form" data-disable-all="readOnly">
    <label>Label_1</label>
    <input name="sDesignName" type="text" maxlength="255" ng-model="studyDesign.studyDesignName"
           class="form-control input-sm" ng-readonly="!isStudyActive"/>

    <label>Label_2</label>
    <input type="text" name="overdueDays" class="form-control input-sm"
           ng-pattern="numericPattern"
           onkeypress='return event.charCode >= 48 && event.charCode <= 57' maxlength="4"
           ng-model="currentTimePoint.overdueDays "
           ng-disabled="currentTimePoint.scheduled=='N'?true:false" ng-required="currentTimePoint.scheduled=='N'?false:true && !readOnly"/>
</form>

For 2nd input I get exception:

TypeError: Cannot read property '$$notifyModelModifiedStateChanged' of null
    at angular-input-modified.js:386
    at angular.js:19612
    at completeOutstandingRequest (angular.js:5964)
    at angular.js:6243

'cause when ngInputModified's ngModel directive is executing the form in null. In ngModelModifiedFactory

  // Handling controllers.
  var modelCtrl = controllers[0];    // ModelController
  var formCtrl = controllers[1];     // null
  var bsModifiable = controllers[2]; // null

Do you have any idea what's go wrong?

$$onChildModelModifiedStateChanged

Hi,
I am getting this console error though it's not causing any issues,
Cannot read property '$$onChildModelModifiedStateChanged' of null

Please let me know how to fix this bug.

Regards

Sandeep

Modified property doesn't carry over to parent form

It appears that child forms do not propagate the modified value to their parent forms. I modified one of your examples to show you what I mean: Example

You'll see that while the modified value of subForm changes the modified value of myForm (subForm's parent) does not.

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.