1

I have an array that looks like this:

$rootScope.array = [{

                    "id":"a",
                    "title":"a",
                    "options":[
                                {
                                  "optId":"abc",
                                  "isInvalid":false
                                },
                                {
                                  "optId":"efg",
                                  "isInvalid":false
                                }
                              ]
                  },
                  {
                    "id":"b",
                    "title":"b",
                    "options":[
                                {
                                  "optId":"hij",
                                  "isInvalid":false
                                },
                                {
                                  "optId":"lmn",
                                  "isInvalid":false
                                }
                              ]
                  }];

On any change to the 'isInvalid' attribute inside 'options', I want to trigger a function that would evaluate the entire array and set some properties to 'true' and some to 'false'.

I understand something of this sort can be done using deep $watch object equivalence, but I am not sure as to how exactly I can watch only for change in the 'isInvalid' and not change in lets say 'optId'.

In the bigger scheme of things, my intent is to validate the entire array by a set of business rules. So, if the "isInvalid" attribute of the first object changes to 'true', I want to make sure that the 'isInvalid' of the second object is set to 'false' and so on.

So if there is a better way to do this other than $watch, I am all ears, because I'm relatively new to Angular and I'm a little lost!

Machavity
  • 30,841
  • 27
  • 92
  • 100
nikjohn
  • 20,026
  • 14
  • 50
  • 86

3 Answers3

4

Rather than using a deep watch (which can silently cause performance issues), create a custom function for your watch expression. The example below iterates through the array elements and options creating one check string containing each item's id, option id and its respective isInvalid value.

The check string will look something like this: "a|abcfalseefgfalseb|hijfalselmnfalse"

If an isInvalid value changes, the check string will change, and the watch will trigger.

   $scope.$watch(function() {
      var check = "";
      for (var i = 0; i < $scope.array.length; i++) {
      var item = $scope.array[i];
      check += item.id + "|"; 
        for (var j = 0; j < item.options.length; j++) {         
          var option = item.options[j];
          check += option.id + item.isInvalid;
        }
      }
      return check;
  }, doSomething());
James Lawruk
  • 30,112
  • 19
  • 130
  • 137
  • will the watch always fire the first time on init with this approach – Justin Apr 27 '17 at 15:23
  • 1
    Yes. To avoid that, refer to this post: http://stackoverflow.com/questions/16947771/how-do-i-ignore-the-initial-load-when-watching-model-changes-in-angularjs – James Lawruk Apr 27 '17 at 17:09
1

If your structure is not too big, a deep watch would do the trick.

http://teropa.info/blog/2014/01/26/the-three-watch-depths-of-angularjs.html

If you are concerned about performance, and depending your scenario, you could evaluate doing something like:

Having a parent/global isInvalid flag, whenever a flag is marked as invalid mark as well the global invalid, then you only have to watch for the global invalid flag and once the flag is invalid traverse to find the not valid element.

Other options that come to my mind could be to add a watch per entry in the array, but not sure if this could have worse performance than the deepwatch solution.

If you don't make a deepwatch abuse, it can be a good solution for your scenario.

Braulio
  • 1,748
  • 14
  • 23
1

If you don't want to use a deep watch. You can define your own watch logic as well. According to angular document. In $watch(expression) expression function will be executed multiple times during $digest. So you can use you own code to detect the change in your huge array.

live demo: http://plnkr.co/edit/X4J5Faoa2ni79RmFa3co?p=preview (open your console to see the log).

Bob Yuan
  • 588
  • 3
  • 14