Установка $scope. $Watch для каждого элемента массива

Я пытаюсь выяснить, как установить $scope.$watch для каждого элемента массива. Меня интересуют только определенные атрибуты каждого элемента.

В моем примере каждый объект rental_date имеет три атрибута: date, start_time и stop_time. Всякий раз, когда start_time изменяется, я хочу, чтобы stop_time был установлен на 2 часа после start_time.

Вызов $ngResource (с использованием 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

Вот четыре варианта функции $watch, которые я пробовал:

1

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

2

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

3

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

4

Это работает, но на данный момент я не могу придумать элегантное решение для доступа только к значениям, которые меня интересуют $watch ing, а не всему массиву.

$scope.$watch 'rental_dates', ((newval, oldval) ->
  $log.info 'watch changed'
  $log.info newval
  $log.info oldval
), true

Кто-нибудь сделал что-то подобное в своих проектах?

Ответ 1

Я предполагаю, что вы каким-то образом используете ng-repeat для генерации элементов пользовательского интерфейса для каждой записи массива? Назначьте ng-контроллер каждому из этих элементов интерфейса. Контроллер будет создан для каждого элемента массива. Внутри этого контроллера используйте $watch.

<div ng-repeat="rentalDate in rental_dates" ng-controller="RentalDateController">
</div>

И затем в вашем коде контроллера:

angular.module(...).controller('RentalDateController', function ($scope) {
    $scope.$watch('rentalDate.start_time', function (startTime) {
        ...
    });
});

Вот небольшой пример: http://plnkr.co/edit/lhJ3MbULmahzdehCxVeM?p=preview

Ответ 2

Если вы хотите посмотреть в определенном поле в объекте, который является частью коллекции, и если у вас есть элементы коллекции, которые повторяются в представлении ng-repeat, и если у вас есть видимое поле, связанное с элементом управления, может иметь ng-change, вы всегда можете обрабатывать наблюдательный аспект с помощью обработчика ng-change. например:

 <div ng-repeat="binding in bindings track by $index">
     <input ng-model="binding" ng-change="changed(binding)">
 </div>

и в js:

 scope.changed = function(binding){
      .....
 }

Ответ 3

Я думаю, что решение номер 3 будет работать, если вы измените первый аргумент $watch на: 'rental_dates['+pos+'].start_time' ($watch принимает выражение angular как первое параметр и pos не входит в вашу область действия).

(Хотя это должно сработать, я думаю, что это будет не очень эффективно. Возможно, вам стоит изучить обновление как start_time, так и stop_time одновременно, вместо просмотра изменений во все времена начала.)