$watch not working on variable from other controll

2020-03-27 07:31发布

I have one controller which displays a checklist, and stores the selection in an array.

My other controller runs an $http.get on the array from the first controller.

How do I set a $watch so that whenever the array changes, a new HTTP GET request is sent?

My attempt: 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'];

3条回答
爷的心禁止访问
2楼-- · 2020-03-27 08:07

In such cases, I recommend using a service for data manipulation and messages to keep the controllers and UI in sync.

Take a look here: AngularJS multiple uses of Controller and rootScope

查看更多
看我几分像从前
3楼-- · 2020-03-27 08:18

By default, a $watch checks for changes to a reference, not for equality. Since objects and arrays still have the same reference when modified, the watch is not triggered. There are at least two options to get it working.

If the only changes you want to be notified of modify the size of the array (adding or removing elements vs. changing the content of an element), you can set the watch on the length property of the array instead like:

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

Otherwise, you can use the optional $watch argument objectEquality, which expects a boolean. This does an equality check rather than a reference check.

$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

This is not the default behavior because it performs a more costly deep check of all the elements so only use when necessary.

查看更多
啃猪蹄的小仙女
4楼-- · 2020-03-27 08:19

Moving some of that logic into the factory, then sending it out to all controllers with a $rootScope.$broadcast will get your information to the correct places.

I moved the array creation into the factory, then used the $broadcast from there:

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}
})

Using $scope.$on in the controllers receives the new data when it changes:

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
})
}

Here's the plunk

查看更多
登录 后发表回答