Javascript AngularJS:$scope.$watch 没有更新从 $resource 获取的自定义指令值
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11135864/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
AngularJS : $scope.$watch is not updating value fetched from $resource on custom directive
提问by Sheniff
I'm having an issue with custom directives that's driving me crazy. I'm trying to create the following custom (attribute) directive:
我遇到了让我发疯的自定义指令问题。我正在尝试创建以下自定义(属性)指令:
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);
});
}
}
});
This "attribute-type" directive (called seatMap) is trying to show a list of seat ids (e.g, for a theatre) which I'll fetch from the server via $resource service (see code below) into a div (element).
这个“属性类型”指令(称为seatMap)试图显示一个座位ID列表(例如,对于剧院),我将通过$resource服务(见下面的代码)从服务器获取到一个div(元素) .
I'm using it with this simple partial html:
我将它与这个简单的部分 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>
And this is the controller which is loading the scope:
这是正在加载范围的控制器:
function SeatsCtrl($scope, Seats) {
$scope.sessionId = "12345";
$scope.zoneId = "A";
$scope.seats = Seats.query({sessionId: $scope.sessionId, zoneId: $scope.zoneId});
$scope.max_seats = 4;
}
Where "Seats" is a simple service using $resources to fetch a JSON from the server
其中“座位”是一个使用 $resources 从服务器获取 JSON 的简单服务
angular.module('myApp.services', ['ngResource'])
.factory('Seats', function($resource){
return $resource('json/seats-:sessionId-:zoneId.json', {}, {});
})
;
app.js (asientos_libres.html is the partial I've been using):
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'});
}]);
The problem is, even though I set up a "scope.$watch" in the link function of the directive so that the scope can check whether "seats" attribute has changed to update the list of ids, it isn't working at the moment $scope.seats is changing in the controller (when we call "query").
问题是,即使我在指令的链接函数中设置了“scope.$watch”,以便范围可以检查“seats”属性是否已更改以更新 id 列表,但它在时刻 $scope.seats 在控制器中发生变化(当我们调用“查询”时)。
As you might see in the code, I made a try using $timeout to delay the launch of "updateSeatInfo", but I'm afraid it's not the smartest solution by far...
正如您可能在代码中看到的那样,我尝试使用 $timeout 来延迟“updateSeatInfo”的启动,但恐怕这不是迄今为止最聪明的解决方案......
I also tried to not to make a JSON request, but use a hard-coded dictionary in $scope.seats and it works, so it seems it's a matter of synchrony.
我也尝试不发出 JSON 请求,而是在 $scope.seats 中使用硬编码字典并且它有效,所以它似乎是一个同步问题。
Note: The updateSeatInfo is just a test function, the actual function I'll use is a bit more complex.
注意:updateSeatInfo 只是一个测试函数,我将使用的实际函数有点复杂。
Any idea about how to cope with it?
关于如何应对它的任何想法?
Thank you a lot beforehand!
事先非常感谢!
Edit 1:Added app.js, where I'm using router to call SeatsCtrl, thanks to Supr for the advice. However, I'm still having the same issue.
编辑 1:添加了 app.js,我使用路由器来调用 SeatsCtrl,感谢 Supr 的建议。但是,我仍然遇到同样的问题。
Edit 2: Solved!(?)Ok! It seems I found a solution, which may not be the best, but it's working properly! :) As far as I could see here http://docs.angularjs.org/api/ng.$timeout, we can use $timeout (a wrapper over setTimeout) with no delay! This is great because we aren't artificially delaying the execution of our code inside $timeout, but we're making the directive not to run it until the asynchronous request has finished.
编辑2:解决了!(?)好的!看来我找到了一个解决方案,它可能不是最好的,但它工作正常!:) 就我在这里看到的http://docs.angularjs.org/api/ng.$timeout 而言,我们可以毫无延迟地使用 $timeout(setTimeout 的包装器)!这很棒,因为我们没有人为地延迟 $timeout 内代码的执行,而是让指令在异步请求完成之前不运行它。
Hope it will work for long-wait requests, too...
希望它也适用于长时间等待的请求......
If someone knows a better way to fix it, please tell!
如果有人知道更好的方法来解决它,请告诉!
回答by Bryan Mundie
The issue is that watch compares the reference instead of the object by default. Add ,true to the end to have it compare the value instead.
问题是 watch 默认比较引用而不是对象。添加 ,true 到最后让它比较值。
scope.$watch('seats', function(newval, oldval){
console.log(newval, oldval);
updateSeatInfo(scope,element);
}, true);
回答by t4ncr3d3
I had this problem too. It was due to the fact that my variable was at first not defined 'undefined' in the scope. Watch seems to not work on undefined variables. Seems obvious after-all.
我也有这个问题。这是因为我的变量起初没有在范围内定义为“未定义”。Watch 似乎不适用于未定义的变量。毕竟似乎很明显。
I was first trying to use watch to trigger when my variable would be effectively set by the controller. Example:
当我的变量由控制器有效设置时,我首先尝试使用 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 = {};
});
});
I solved it by initializing my variable with an empty object before calling the service:
我通过在调用服务之前用一个空对象初始化我的变量来解决它:
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 = {};
});
});
In that case watch was able to detect the change in the variable.
在那种情况下 watch 能够检测到变量的变化。
回答by Supr
I can't see that you're using the SeatsCtrl
-controller anywhere? How is it being used? And have you verified that it is activated, and that the query is actually performed?
我看不出你在SeatsCtrl
任何地方都在使用-controller?它是如何使用的?您是否已验证它已激活,并且查询已实际执行?
The quickest way to check if SeatsCtrl is in use is to simply add a console.log('SeatsCtrl actived!');
inside it. If it is not, then add ng-controller="SeatsCtrl"
to the div
.
检查是否正在使用 SeatsCtrl 的最快方法是简单地console.log('SeatsCtrl actived!');
在其中添加一个。如果不是,则添加ng-controller="SeatsCtrl"
到div
.
You can also put a watch-and-log on the seats directly inside the controller just to make sure it is not an issue with scoping.
您还可以直接在控制器内部的座位上放置一个手表和日志,以确保它不是范围界定的问题。
回答by Joe Martinez
I have this problem too. I think it's a problem in Angular. There is a ticket on GitHub to "beef up $resource futures"which would probably address this issue, as what you really need is access to the promise object for the resource you have.
我也有这个问题。我认为这是 Angular 的问题。GitHub 上有一张“加强 $resource futures”的票,它可能会解决这个问题,因为您真正需要的是访问您拥有的资源的 promise 对象。
Or, the watchers could wait to fire until the promise is resolved.
或者,观察者可以等到承诺得到解决后再触发。
In the meantime, a slightly more elegant way to fix this which doesn't require a timeout is to reassign the scope property that is being watched from the success callback on the $resource. This will cause the watcher to fire again at the appropriate time.
与此同时,一个稍微更优雅的解决这个问题的方法是重新分配 $resource 上成功回调正在监视的范围属性。这将导致观察者在适当的时候再次触发。
A timeout with no delay is just putting the evaluation of the deferred function on the end of the current stack - in your case, your resource happened to be resolved by then, but this could be a problem if the server is having a case of the Mondays.
没有延迟的超时只是将延迟函数的评估放在当前堆栈的末尾 - 在您的情况下,您的资源恰好到那时已解决,但是如果服务器遇到以下情况,这可能是一个问题星期一。
Example:
例子:
$scope.myAttr = MyResource.fetch(
function (resource) { $scope.myAttr = new MyResource(angular.copy(resource)); }
);