53

I have this data in my controller

 $scope.data = { home: { baseValue: "1", name: "home" }, contact: { baseValue: "2", name: "contract" } // a lot more options }; 

with some html like this:

<section class="content row" ng-repeat="item in data"> {{item.name}} .... </section> 

Now, I want to know when the baseValue is changed but because of I using a objects inside the data variable I can not watch the property in a simpler way.

I have tried something like this, but I have to loop all the array

$scope.$watch('data', function (newValue, oldValue, scope) { // some code to compare the tow arrays, line by line }, true); 

How can I do a simpler $watch to know only when the baseValue is changed?

Similar questions:

UPDATE 1

I could add an individual watch for each object to know when the baseValue is changed, but it won't be nice if I had an n number of objects, not only a couple of objects like in this example

$scope.$watch('data.home', function (newValue, oldValue, scope) { // do some stuff with newvalue.baseValue }, true); $scope.$watch('data.contact', function (newValue, oldValue, scope) { // do some stuff with newvalue.baseValue }, true); ... // Adds more individual `watch` 
5
  • You want to only know when baseValue has changed but you don't want to add an individual watch for it? Commented Jul 22, 2014 at 0:28
  • About your update 1, can you define "not nice" ? Commented Jul 22, 2014 at 0:34
  • how about using property descriptors, and falling back to *Timeout's? Commented Jul 22, 2014 at 0:40
  • Do you need to know old value? Or you just need to watch for changes? Commented Jul 22, 2014 at 0:40
  • Or $watchGroup ? A variant of $watch() where it watches an array of watchExpressions. If any one expression in the collection changes the listener is executed. Commented Jul 22, 2014 at 1:05

4 Answers 4

35

Based on your question, you can use ngChange to watch changes of baseValue and trigger the function.

HTML

<section class="content row" ng-repeat="item in data"> Name: {{item.name}} <br/> BaseValue: <input type="text" ng-model="item.baseValue" ng-change="baseValueChange(item.baseValue)"/> </section> 

Controller

$scope.baseValueChange = function(baseValue) { console.log("base value change", baseValue); } 

If you a more sophisticated version which can get oldValue and newValue, you can refer to this plunkr - http://plnkr.co/edit/hqWRG13gzT9H5hxmOIkO?p=preview

HTML

<section class="content row" ng-repeat="item in data"> Name: {{item.name}} <br/> BaseValue: <input type="text" ng-init="item.oldBaseValue = item.baseValue" ng-model="item.baseValue" ng-change="baseValueChange(item.oldBaseValue, item.baseValue); item.oldBaseValue = item.baseValue"/> </section> 

Controller

$scope.baseValueChange = function(oldVal, newVal) { console.log("base value change", oldVal, newVal); } 
Sign up to request clarification or add additional context in comments.

4 Comments

This worked for me, but isn't true that by using ng-change the performance could be impacted? This is because the function would be executed not only when the value is changed. For that reason it is better to use a wath to ensure that the function is executed only when the data is updated. Is that correct?
I think not much different compare to watch. You might want to compare number of listener register in between ng-change and watch. I think is the same.
I agree with +Coyolero. The solution that @Jerska offered fits the question better in my case
I dont think this is a good answer as it will only be triggered on updates from the input control and not from code
16

You can watch an object attribute.
So you can do something like

for(var key in $scope.data) { if($scope.data.hasOwnProperty(key)) { $scope.$watch("data['" + key + "'].baseValue", function(val, oldVal) { // Do stuff }); } } 

Not tested, but the idea is simple.

8 Comments

I just started learning AngularJS two days ago. I love it so far. This solution is exactly what I need. I noticed that the ng-change directive only works with values changed by interacting with bound inputs (checkboxes in my case). I want do something when the array's elements baseValues change, regardless of whether they changed from bound inputs or by programmatically toggling them.
If the items are coming and going, you should also remove the stale watchers: var w = $scope.$watch(...); and later just call w(); to unregister.
$scope.$watch("data[" + key + "].baseValue" .....) didnt work for me, but $scope.$watch("data['" + key + "'].baseValue" .....) did
Just a safety thing, I'd do "data[" + JSON.stringify(key) + "].baseValue" instead of "data['" + key + "'].baseValue"
@Hashbrown Agreed. I feel like if you're not aware of this trick to escape strings, it might end up more confusing, so I won't change the answer, but hopefully your comment will be upvoted and easily seen.
|
6

In this kind of scenario there is no way to circumvent utilizing multiple watches, another way to do this is by utilizing $watchCollection to watch the array of object values, you can get this array using the Object.values function.

scope.$watchCollection(function() { return Object.values(obj); }, function(newValues, oldValues) { // now you are watching all the values for changes! // if you want to fire a callback with the object as an argument: if (angular.isFunction(scope.callback())) { scope.callback()(obj); } }); 

Comments

2

I solved with this solution:

$scope.updateFields= function(){ angular.forEach($scope.fields,function (value, key) { value.title = value.title.toLowerCase().replace(/\s+/g,''); }) }; $scope.$watch('fields', $scope.updateFields, true); 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.