$watch не работает с переменной из другого контроллера?

У меня есть один контроллер, который отображает контрольный список и сохраняет выбор в массиве.

Мой другой контроллер запускает $http.get в массиве с первого контроллера.

Как установить $watch, чтобы при каждом изменении массива отправлялся новый HTTP-запрос GET?

Моя попытка: http://plnkr.co/edit/EaCbnKrBQdEe4Nhppdfa

// See plnkr for other controller + FooSelection factory + view
function SimpleQueryResCtrl($scope, $http, FooSelection) {
    $scope.foo_list_selection = FooSelection;

    $scope.$watch('foo_list_selection', function (newValue, oldValue) {
        if (newValue !== oldValue)
            $http.get('/api/' + $scope.foo_list_selection).success(function (largeLoad) {
                $scope.myData = largeLoad;
            });
    });
}

SimpleQueryResCtrl.$inject = ['$scope', '$http', 'FooSelection'];

person Foo Stack    schedule 02.06.2013    source источник
comment
Я думаю, что ваш код правильный, вы получаете ошибки? Вы также можете проверить, является ли newValue !== oldValue, чтобы убедиться, что ваш запрос запущен только тогда, когда «foo_list_selection» действительно изменяется.   -  person Bertrand    schedule 02.06.2013
comment
Хорошо, я считаю, что это самый простой работающий пример, который я могу сделать: plnkr. Вы должны увидеть в своей консоли, что HTTP-запросы были сделаны, но из этого примера не было сделано ни одного.   -  person Foo Stack    schedule 02.06.2013
comment
Я исправил некоторые ошибки в вашем плункере, кажется, он работает нормально: ?p=предварительный просмотр   -  person joakimbl    schedule 02.06.2013
comment
У меня это работает, каждый раз, когда я набираю что-то на входе, запрос на получение, если его запускают в /api/ + foo_list_selection.   -  person Bertrand    schedule 02.06.2013
comment
Спасибо, ваш образец работает. К сожалению, когда я переключаюсь с тега input на тег флажка, он терпит неудачу.   -  person Foo Stack    schedule 02.06.2013
comment
Этот плункер plnkr.co/edit/3ExrhzF9sD8bucXiNuRv?p=preview работает с использованием флажка .   -  person Paul Ryan    schedule 02.06.2013
comment
Спасибо, но, к сожалению, моя проблема связана с разделением массива между контроллерами. Вот моя версия, которая не работает.   -  person Foo Stack    schedule 02.06.2013
comment
Я отредактировал вопрос, чтобы правильно выделить проблему.   -  person Foo Stack    schedule 02.06.2013
comment
Добавление true в качестве третьего параметра к $watch похоже работает (как уже предлагал @sh0ber). Я использовал $timeout вместо $http для имитации активности сервера: рабочий плункер.   -  person Mark Rajcok    schedule 03.06.2013


Ответы (3)


По умолчанию $watch проверяет наличие изменений в ссылке, а не равенство. Поскольку объекты и массивы по-прежнему имеют одну и ту же ссылку при изменении, часы не запускаются. Есть как минимум два варианта заставить его работать.

Если единственные изменения, о которых вы хотите получать уведомления, изменяют размер массива (добавление или удаление элементов по сравнению с изменением содержимого элемента), вы можете вместо этого установить часы для свойства длины массива, например:

$scope.$watch('foo_list_selection.length', function (newValue, oldValue) {
// ...

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

$scope.$watch('foo_list_selection', function (newValue, oldValue) {
    if (newValue !== oldValue)
        $http.get('/api/' + $scope.foo_list_selection).success(function (largeLoad) {
        $scope.myData = largeLoad;
    });
}, true);  // <- put `true` here

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

person Dan    schedule 02.06.2013
comment
Спасибо, совершенно уверен, что это ответ, который я ищу. Не удалось заставить его работать с этим примером, возможно, вы могли бы помочь? - Я обернул строки 13-40 файла main.js с этим условием $scope.$watch для введенной переменной. in (и введен в $scope). Это не сработало :(. Это из примера пейджинга ng-grid Кстати. - person Foo Stack; 02.06.2013
comment
Эти строки кажутся определениями функций, поэтому я думаю, что они, вероятно, не то, что вам действительно нужно в корпусе часов. Возможно, вы могли бы опубликовать свой новый код (с оберткой часов) и новый вопрос о том, что вы пытаетесь сделать. - person Dan; 02.06.2013

Перемещение части этой логики на фабрику, а затем отправка ее всем контроллерам с помощью $rootScope.$broadcast позволит получить вашу информацию в нужных местах.

Я переместил создание массива в фабрику, а затем использовал оттуда $broadcast:

myApp.factory('FooSelection', function ($rootScope) {
var tempArr = [];
var fixArray = function(item){
  if (tempArr.indexOf(item) === -1){
    tempArr.push(item);
  } else {
    tempArr.splice(tempArr.lastIndexOf(item), 1);
  }
  $rootScope.$broadcast('newArray', tempArr);

}

return {fixArray: fixArray}
})

Использование $scope.$on в контроллерах получает новые данные при их изменении:

function SimpleQueryResCtrl($scope, $http, FooSelection) {
$scope.foo_list_selection = FooSelection;

$scope.$on('newArray', function(evt, message){
  console.log(message) // and you can put your $get method here
})
}

Вот пример

person rGil    schedule 02.06.2013

В таких случаях я рекомендую использовать службу для обработки данных и сообщений, чтобы синхронизировать контроллеры и пользовательский интерфейс.

Посмотрите здесь: множественное использование контроллера и rootScope в AngularJS

person David Riccitelli    schedule 03.06.2013