Angular 当从控制器调用的服务更改时不更新视图

Angular not updating view when changed by service called from controller

似乎当我在指令中使用由控制器调用的服务来更新主控制器(示例中的 MyController)中的数组时,Angular 不会更新视图。

但是,如果我将服务排除在外,让指令直接更新数组,那么视图就会更新。

(在下面的示例中,在 MyDirectivesController 中有 2 行,其中一行被注释掉了,这是有效的版本 - 直接更改 gridInDirective....它下面的行调用了 myService,它确实获取了 gridInDirective 数组本地,并且似乎确实在本地更改它,但视图未更新)

如果你 运行 这个例子的想法是你点击彩色 div 并且数组的元素,由 ng-repeat 循环打印,被改变。

我已经在几个地方尝试过使用 $scope.$apply() 但 (a) 没有用,并且 (b) 我的理解是只有在我在 Angular...我不认为我是。

所以问题是,我如何获取在 myService 中更新的网格版本以更新视图,以及为什么它不像我拥有的​​那样工作?

提前感谢任何花时间看一看的人。

'use strict';

(function() {
    var ssheet = angular.module('ssheet', []);
    
    ssheet.factory('myService', [function () {
        var myServiceFunction;
        
        return {
            myServiceFunction: function (grid) {
                alert('myServiceFunction');
                alert('grid given to myServiceFunction is ' + grid.toString());
                grid = ['now', 'the', 'grid', 'has', 'changed'];
                alert('grid in myServiceFunction changed to ' + grid.toString());
            } 
        }
    }]);
    
    ssheet.controller('MyController', ['$scope', function ($scope) {
        $scope.gridInMyController = ['foo','bar','dog','cat']; // this was a 2D array, but simplified it for testing
    }]);
    
    ssheet.controller('MyDirectivesController', ['myService', '$scope', function (myService, $scope) {
        $scope.changeGrid = function () {    
            alert('MyDirectivesController.changeGrid');
            //$scope.gridInDirective = ['now', 'the', 'grid', 'has', 'changed']; //this worked
            myService.myServiceFunction($scope.gridInDirective); // this doesn't work
        }
    }]);
    
    ssheet.directive('gridTools', ['myService', function (myService) {       
        return {
            
            restrict: 'E',
            replace: true,
            scope: {
                gridInDirective: '=gridInElement',
            },
            template: '<div class="grid-tools-style" grid-in-element="gridInMyController" ng-click="changeGrid()">Click this div to change the grid</div>',
            
            controller: 'MyDirectivesController',
        }
    }]);
    
})();
.grid-tools-style {
    background-color: #c68700;
}

.grid-tools-style:hover {
    cursor: pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="ssheet">
    <div ng-controller="MyController">
        <grid-tools></grid-tools>
        <div ng-repeat="item in gridInMyController"><p>{{item}}</p></div>
    </div>
</body>

您可以 return 指令控制器方法中的新网格并将其分配给指令网格的范围变量。

请参阅下面和此 jsfiddle 中的演示。

'use strict';

(function() {
  var ssheet = angular.module('ssheet', []);

  ssheet.factory('myService', [
    function() {
      //var myServiceFunction;

      return {
        myServiceFunction: function(grid) {
          //alert('myServiceFunction');
          //alert('grid given to myServiceFunction is ' + grid.toString());
          grid = ['now', 'the', 'grid', 'has', 'changed'];
          //alert('grid in myServiceFunction changed to ' + grid.toString());
          return grid;
        }
      }
    }
  ]);

  ssheet.controller('MyController', ['$scope',
    function($scope) {
      $scope.gridInMyController = ['foo', 'bar', 'dog', 'cat']; // this was a 2D array, but simplified it for testing
    }
  ]);

  ssheet.controller('MyDirectivesController', ['myService', '$scope',
    function(myService, $scope) {
      $scope.changeGrid = function() {
        //alert('MyDirectivesController.changeGrid');
        //$scope.gridInDirective = ['now', 'the', 'grid', 'has', 'changed']; //this worked
        $scope.gridInDirective = myService.myServiceFunction($scope.gridInDirective); // this doesn't work
        console.log($scope.gridInDirective);
      }
    }
  ]);

  ssheet.directive('gridTools', ['myService',
    function(myService) {
      return {

        restrict: 'E',
        replace: true,
        scope: {
          gridInDirective: '=gridInElement',
        },
        template: '<div class="grid-tools-style" grid-in-element="gridInMyController" ng-click="changeGrid()">Click this div to change the grid</div>',

        controller: 'MyDirectivesController',
      }
    }
  ]);

})();
.grid-tools-style {
  background-color: #c68700;
}
.grid-tools-style:hover {
  cursor: pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="ssheet">
  <div ng-controller="MyController">
    <grid-tools></grid-tools>
    <div ng-repeat="item in gridInMyController">
      <p>{{item}}</p>
    </div>
  </div>
</div>

它不起作用的原因是您通过值将数组的引用传递给myServiceFunction函数,这意味着您可以修改它内容,但不能更改其对新数组的引用,这实际上就是这一行的作用:

grid = ['now', 'the', 'grid', 'has', 'changed'];

它在指令中起作用的原因是因为您有可用的实际引用,您可以将其更改为指向其他内容。

这是您可以保留参考的方法:

我写了一个简短的函数,让您可以将数据放入 Object 而无需替换它。 你可以在你的服务中使用它。这样你就不需要使用手表了。您可以使用 Angular 的正常摘要循环。 例如,查看这个简单的控制器和服务:

演示:JSFidde - http://jsfiddle.net/y09kx7f7/1/ 断言测试

控制器:

app.controller('dem',function($scope,me){
    $scope.user=me.user;   // {name:'user name'}
})

每次更改用户名后,手柄会自动切换,无需观看!

服务

.factory('me',function($timeout){
    var obj={}
    obj.user={name:'hello'}
    $timeout(function(){  //Example of change the data
        newUser={name:'israel'}
        cloneInto(obj.user,newUser)
    },1000)
    return obj
})

我写的cloneInto函数:

// The function it is like a=b, 
// but keeping the object in his original memory position.
function cloneInto(a,b){
    var i
    for (i in a){
        if(typeof a[i]!='object') //For supporting deep-copy
           delete a[i]
    }
    for (i in b){
        if (typeof b[i]=='object'){
            if(!a[i]) a[i]={}
            cloneInto(a[i],b[i])
        }
        else{
            a[i]=b[i]
        }
    }
}