How can I denote which input fields have changed in AngularJS
Asked Answered
B

7

37

I'm working on an edit form (user.html) that PUTs data to an API, but I'd like to avoid PUTting all the data in the form. I'd like to PUT just the changed items.

I've seen the use of dirty and pristine when working with forms, but this applies to any change in the form. I've also seen the use of ng-change, but I don't want to trigger an action on a change to one element, just denote that the changed element should be included in the PUT.

Anyone found a way to denote only the input fields that have changed?

Budge answered 5/9, 2013 at 16:35 Comment(0)
P
54

If you put the input in a form with a name attribute and then give the input a name attribute, you can also access the input's $pristine property.

<div ng-controller="MyController">
  <form name="myForm">
    <input type="text" name="first" ng-model="firstName">
    <input type="text" name="last" ng-model="lastName">
  </form>
</div>
app.controller('MyController', function($scope) {
  // Here you have access to the inputs' `$pristine` property
  console.log($scope.myForm.first.$pristine);
  console.log($scope.myForm.last.$pristine);
});

You can use $scope.myForm.$pristine to see if any fields have changed, and the $pristine property on each input's property on the form to see if that input has changed. You can even iterate over the myForm object (non-input-field objects have keys prefixed with a $):

angular.forEach($scope.myForm, function(value, key) {
  if(key[0] == '$') return;
  console.log(key, value.$pristine)
});
// first, true
// last, false
Phonography answered 5/9, 2013 at 16:46 Comment(4)
Let's say the user enters the page and the firstName field is blank. $scope.firstName.$pristine is set to true at this point. The user then enters "foo". $scope.firstName.$pristine is now set to false. The user then backspaces three times to leave the input empty, as it originally was. $scope.firstName.$pristine continues to be set to false.Mb
@WalterRoman That's correct. $pristine is defined in the docs as: "True if user has not interacted with the form yet"Phonography
I would like to set the focus or update the background color of updated element. How can I do that using 'value' object? or which object should i use and which property?Subplot
Is there any option for identify the scope value changed in check box and select drop down using $pristine , Not getting the scope fields when iterating through formGarrygarson
H
19

I often find that you will want more functionality when allowing users to update settings/information. Such as the ability to reset the information or cancel the edit and revert back. I know that was not part of the request, but when you consider this it makes other things easier.

You store the saved values and also have the edited values, you can reset back to the saved values as they don't change. Then you can compare the 2 to determine what changed.

Working Example: http://jsfiddle.net/TheSharpieOne/nJqTX/2/

Look at the console log to see what changed when you submit the form in the example. It is an object that you can easily send via PUT.

function myCtrl($scope) {
    $scope.user = {
        firstName: "John",
        lastName: "Smith",
        email: "[email protected]"
    };
    $scope.reset = function () {
        angular.copy($scope.user, $scope.edit);
    };
    $scope.submitForm = function(){
        console.log(findDiff($scope.user, $scope.edit));
        // do w/e to save, then update the user to match the edit
        angular.copy($scope.edit, $scope.user);
    };

    function findDiff(original, edited){
        var diff = {}
        for(var key in original){
            if(original[key] !== edited[key])
                diff[key] = edited[key];
        }
        return diff;
    }
}

Note: the findDiff is simple, it assume the two objects have the same keys and only the values have changed. We copy the objects so that they do not become 2 references to the same object, but in fact 2 objects.

Hydrous answered 5/9, 2013 at 17:38 Comment(2)
You could use angular.copy(source,dest) instead of writing your own clone function. I do like your approach though.Gildea
I forgot about that. +1 to you good sir. (Edited answer to reflex this)Hydrous
T
8

old thread but to build on TheSharpieOne's answer, you may want to check for equality using angular.equals instead of "===" otherwise this won't work for arrays.

function findDiff(original, edited){
  var diff = {}
    for(var key in original){
      if(!angular.equals(original[key], edited[key]))
        diff[key] = edited[key];
    }
    return diff;
}
Turbot answered 31/8, 2015 at 14:44 Comment(0)
S
2

You can use $scope.$watch('scopeVariable', function(oldValue, newValue)...) and build an object containing only newValues that are different than oldValues.

Here's a link to Angular docs regarding $watch.

Shanon answered 5/9, 2013 at 16:40 Comment(0)
H
1

Building off ARN and TheSharpieOne's answers. If you are using underscore in your project you could take this approach for finding differences in arrays of objects.

function findDiff(original, edited){
    _.filter(original, function(obj){ return !_.findWhere(edited, obj); });
}
Hazlip answered 7/4, 2016 at 17:37 Comment(0)
J
0

A simple way to retrieve an object only with changed values on submit event:

var dirtyInput = $('#myForm .ng-dirty');
var change = {};

for (var i = 0; i < dirtyInput.length; i++) {
  change[dirtyInput[i].name] = dirtyInput[i].value;
}
Jeanene answered 23/11, 2016 at 14:25 Comment(0)
S
0

Adding more to TheSharpieOne's answer. The diff between original and edited could also be due to new fields added in the edited object. Hence additional check for the same

function findDiff(original, edited){
    var diff = {}
    for(var key in original){
      if(!angular.equals(original[key], edited[key]))
        diff[key] = edited[key];
    }
    for(var key in edited){
      if(!angular.equals(original[key], edited[key]))
        diff[key] = edited[key];
    }

    return diff;
}
Stenger answered 27/1, 2018 at 9:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.