AngularJS:$ scope.$ watch不会更新自定义指令中从$ resource获取的值

She*_*iff 23 javascript json watch angularjs

我遇到了一个让我疯狂的自定义指令的问题.我正在尝试创建以下自定义(属性)指令:

angular.module('componentes', [])
    .directive("seatMap", function (){
        return {
            restrict: 'A',
            link: function(scope, element, attrs, controller){

                function updateSeatInfo(scope, element){
                    var txt = "";
                    for (var i in scope.seats)
                        txt = txt + scope.seats[i].id + " ";
                    $(element).text("seat ids: "+txt);
                }

                /* 
                    // This is working, but it's kind of dirty...
                    $timeout(function(){updateSeatInfo(scope,element);}, 1000);
                */

                scope.$watch('seats', function(newval, oldval){
                    console.log(newval, oldval);
                    updateSeatInfo(scope,element);
                });
            }
        }
    });
Run Code Online (Sandbox Code Playgroud)

这个"属性类型"指令(称为seatMap)试图显示我将从服务器通过$ resource service(参见下面的代码)获取到div(元素)的座位ID列表(例如,用于剧院) .

我正在使用这个简单的部分html:

<div>
    <!-- This is actually working -->
    <ul>
        <li ng-repeat="seat in seats">{{seat.id}}</li>
    </ul>

    <!-- This is not... -->
    <div style="border: 1px dotted black" seat-map></div>
</div>
Run Code Online (Sandbox Code Playgroud)

这是加载范围的控制器:

function SeatsCtrl($scope, Seats) {
    $scope.sessionId = "12345";
    $scope.zoneId = "A";
    $scope.seats = Seats.query({sessionId: $scope.sessionId, zoneId: $scope.zoneId});
    $scope.max_seats = 4;
}
Run Code Online (Sandbox Code Playgroud)

"Seats"是一个使用$ resources从服务器获取JSON的简单服务

angular.module('myApp.services', ['ngResource'])
    .factory('Seats', function($resource){
        return $resource('json/seats-:sessionId-:zoneId.json', {}, {});
    })
;
Run Code Online (Sandbox Code Playgroud)

app.js(asientos_libres.html是我一直在使用的部分):

angular.module('myApp', ['myApp.filters', 'myApp.services', 'myApp.directives', 'componentes']).
  config(['$routeProvider', function($routeProvider) {
    $routeProvider.when('/view1', {templateUrl: 'partials/asientos_libres.html', controller: SeatsCtrl});
    $routeProvider.otherwise({redirectTo: '/view1'});
  }]);
Run Code Online (Sandbox Code Playgroud)

问题是,即使我在指令的链接功能中设置了"范围.$ watch",以便范围可以检查"席位"属性是否已更改以更新ID列表,但它不在$ scope.seats在控制器中发生变化(当我们调用"查询"时).

正如您在代码中看到的,我尝试使用$ timeout来延迟"updateSeatInfo"的发布,但我担心它不是迄今为止最聪明的解决方案......

我也尝试不发出JSON请求,但在$ scope.seats中使用硬编码字典并且它可以工作,所以看起来这是同步问题.

注意:updateSeatInfo只是一个测试函数,我将使用的实际函数有点复杂.

有关如何应对它的任何想法?

非常感谢你!

编辑1:添加app.js,我正在使用路由器调用SeatsCtrl,感谢Supr的建议.但是,我仍然遇到同样的问题.

编辑2:解决了!(?) 好的!我似乎找到了一个解决方案,它可能不是最好的,但它正常工作!:)至于我在这里可以看到http://docs.angularjs.org/api/ng.$timeout,我们可以使用$ timeout(setTimeout上的包装器),没有延迟!这很好,因为我们并没有人为地延迟$ timeout内部代码的执行,但我们正在使指令在异步请求完成之前不运行它.

希望它也适用于长期等待请求......

如果有人知道更好的解决方法,请告诉我!

小智 56

问题是watch默认比较引用而不是对象.添加,true到最后让它比较值.

scope.$watch('seats', function(newval, oldval){
                console.log(newval, oldval);
                updateSeatInfo(scope,element);
            }, true);
Run Code Online (Sandbox Code Playgroud)

  • 需要注意的是:像这样的深度比较会产生性能成本.深度观察对象集合而不是单个对象时,这主要是一个问题,但值得记住. (6认同)

t4n*_*3d3 5

我也有这个问题。这是因为我的变量起初没有在范围内定义为“未定义”。Watch 似乎不适用于未定义的变量。毕竟似乎很明显。

当我的变量由控制器有效设置时,我首先尝试使用 watch 来触发。例子:

myApp.controller('Tree', function($scope, Tree) {

Tree.get({},
function(data) { // SUCCESS
    console.log("call api Tree.get succeed");
    $scope.treeData = data;
},

function(data) { // FAILURE
    console.log("call api Tree.get failed");
    $scope.treeData = {};
});
});
Run Code Online (Sandbox Code Playgroud)

我通过在调用服务之前用一个空对象初始化我的变量来解决它:

myApp.controller('Tree', function($scope, Tree) {

$scope.treeData = {};      // HERE

Tree.get({},
function(data) { // SUCCESS
    console.log("call api Tree.get succeed");
    $scope.treeData = data;
},

function(data) { // FAILURE
    console.log("call api Tree.get failed");
    $scope.treeData = {};
});
});
Run Code Online (Sandbox Code Playgroud)

在那种情况下 watch 能够检测到变量的变化。