Reputation: 3224
I am trying to figure out how to set a $scope.$watch
on each element in an array. I only care about certain attributes of each element.
In my example, each rental_date
object has three attributes: date
, start_time
, and stop_time
. Whenever the start_time
is changed, I want the stop_time
to be set to 2 hours after the start_time
.
The $ngResource
call (using Angular 1.1.5):
Agreement.show(
id: 5
).$then ((success) ->
$scope.agreement = success.data.agreement
# returns an array of rental dates
$scope.rental_dates = success.data.rental_dates
# $watch goes here
Here are the four variations of the $watch
function I tried:
angular.forEach $scope.rental_dates, (date, pos) ->
$scope.$watch date.start_time, ((newval, oldval) ->
$log.info 'watch changed'
$log.info newval
$log.info oldval
), true
angular.forEach $scope.rental_dates, (date, pos) ->
$scope.$watch $scope.rental_dates[pos].start_time, ((newval, oldval) ->
$log.info 'watch changed'
$log.info newval
$log.info oldval
), true
angular.forEach $scope.rental_dates, (date, pos) ->
$scope.$watch 'rental_dates[pos].start_time', ((newval, oldval) ->
# also tried '$scope.rental_dates[pos].start_time'
$log.info 'watch changed'
$log.info newval
$log.info oldval
), true
This does work, but, at the moment, I can't think of an elegant solution to access only the values I care about $watch
ing instead of the whole array.
$scope.$watch 'rental_dates', ((newval, oldval) ->
$log.info 'watch changed'
$log.info newval
$log.info oldval
), true
Has anybody done something similar in their own projects?
Upvotes: 13
Views: 23456
Reputation: 1745
If you want to watch on a specific field in an object that is part of a collection and if you have the collection's items iterated in the view by ng-repeat and if you have the field to be watched bound to a control that can have ng-change, you can always handle the watching aspect using the ng-change handler. eg:
<div ng-repeat="binding in bindings track by $index">
<input ng-model="binding" ng-change="changed(binding)">
</div>
and in js:
scope.changed = function(binding){
.....
}
Upvotes: 8
Reputation: 916
I am assuming you're somehow using ng-repeat to generate UI elements for each entry of the array? Assign an ng-controller to each of these UI elements. The controller will be instantiated for each array element. Inside that controller, use $watch.
<div ng-repeat="rentalDate in rental_dates" ng-controller="RentalDateController">
</div>
And then in your controller code:
angular.module(...).controller('RentalDateController', function ($scope) {
$scope.$watch('rentalDate.start_time', function (startTime) {
...
});
});
Here is a little example: http://plnkr.co/edit/lhJ3MbULmahzdehCxVeM?p=preview
Upvotes: 27
Reputation: 2221
I think solution number 3 will work if you change the first $watch
argument to: 'rental_dates['+pos+'].start_time'
($watch takes an angular expression as the first parameter, and pos isn't part of your scope).
(While this should work, I think this won't be really efficient. Maybe you should look into updating both start_time and stop_time at the same time instead of watching for changes on all the start times.)
Upvotes: 8