我有一个奇怪的错误,我无法弄明白。我最近重构了我的角度应用程序更多'angular-ish'。部分原因是将窗口调整大小事件处理程序移出指令并将其放入.run块中,它可以将Angular事件广播到整个应用程序。
那部分似乎工作正常。它监视窗口并正确传播事件。但是,当我从$ on侦听器调用我的模型操作代码时,数据绑定似乎不会触发。
.controller('channelListController', function ($scope, $rootScope, $http, $filter){
$http.get('api/v1/channels').success(function(data){
$scope.baseChannels = data;
filterChannelData($scope.filter);
//call manually since initial window resize occurs before async return
splitChannelData($rootScope.windowAttr.columns);
});
//*************
//this part doesn't trigger databinding update
//*************
$scope.$on('columnChange', function(){
//window resize requires changing column number
console.log('change detected');
splitChannelData($rootScope.windowAttr.columns);
});
//*************
//but this does
//*************
$scope.$watch('filter', function(newVal){
//the filters are changed
filterChannelData(newVal);
//re-run split for new data
splitChannelData($rootScope.windowAttr.columns);
}, true);
function filterChannelData(filter){
if(filter){
$scope.filteredChannels = $filter('looseCreatorComparator')($scope.baseChannels, filter.creators);
$scope.filteredChannels = $filter('looseTagComparator')($scope.filteredChannels, filter.tags);
} else {
$scope.filteredChannels = $scope.baseChannels;
}
}
function splitChannelData(columns){
if(columns){
$scope.splitChannels = [];
for(var rep=0;rep<columns;rep++){
$scope.splitChannels.push([]);
}
_.forEach($scope.filteredChannels, function(channel, index){
$scope.splitChannels[index % columns].push(channel);
});
}
}
});
因此,当我更改过滤器输入字段的内容时,$ watch会捕获它,运行我的过滤器然后触发splitChannelData。后者将我的数据集重构为一组单独的数组以供显示。视图数据绑定到$ scope.splitChannels。这很好。
但是,当广播'columnChange'事件被$ on捕获时,splitChannelData函数会触发,正确重构$ scope.splitChannels,但视图不会刷新以反映更改。
此外,如果我随后对过滤器输入字段进行另一次更改并触发$ watch处理程序,则会正确更新视图,包括$ on所做的数据集更改但未更新到早点查看。
WUT。
所以,任何人都可以告诉我为什么从$ scope调用splitDataChannel。$ watch使数据绑定正常工作但是$ scope触发的同一个调用。$ on不会?
修改: 我刚刚解决了这个bug,但仍然不明白为什么这个解决方案是必要的。我仍然希望得到反馈。
我在$ on处理程序中添加了$ apply调用:
$scope.$on('columnChange', function(){
//window resize requires changing column number
console.log('change detected');
splitChannelData($rootScope.windowAttr.columns);
//added this
$scope.$apply();
});
为什么$ watch会触发数据绑定更新,而$ on则没有,而latte需要手动$ apply才能调用?
答案 0 :(得分:3)
“ Angular世界的界限”并不像听起来那么复杂。
有点简化,Angular的脏检查通过将观察者存储在数组中来实现。观察者有一个watchExpression和一个listenerFunction。当触发摘要周期时,观察者被迭代。如果watchExpression的返回值自上次以来发生了更改,则会执行关联的listenerFunction。
例如,watchExpression返回字符串hello
,而上次返回hell
。执行listenerFunction以使用返回值hello
更新特定DOM元素的文本值。
如果您更改了已观看但未触发摘要周期的变量,则更新将不会反映在用户界面中。
如果操作触发摘要周期 - 您内部边界。
如果操作未触发摘要周期 - 您在之外边界。
Angular中的大多数常用功能会在内部为您自动触发摘要循环。例如指令ngClick
和$http
服务。
$broadcast
,$emit
和$on
不会触发摘要周期。问题不在于您的处理程序是否附加到$on
,而是最有可能使用$broadcast
的地方。
我的猜测是它看起来像这样:
angular.element($window).on('resize', function () {
$rootScope.$broadcast('columnChange');
});
on
函数是jqLite / jQuery函数。它不会触发消化周期,这意味着它生活在Angular世界的边界之外。
您应该将其打包到$apply
:
angular.element($window).on('resize', function() {
$rootScope.$apply(function () {
$rootScope.$broadcast('columnChange');
});
});
不在$apply
附加的处理程序中调用$on
的原因是因为如果某些内容都调用$broadcast('columnChange')
并触发摘要循环,则会出现$digest already in progress
错误。