为什么此示例中需要$rootScope.$apply()
才能使用页面上的ng-hide
更新元素?
根据我的经验,每当我将$scope.$apply()
置于$scope.$watch
内时,我都会收到“正在进行的摘要”错误。这有什么不同吗?
app.component('myComponent', {
controller: function(){
$scope.visible = false;
$rootScope.$on('someEvent', function(){
$scope.visible = true;
$rootScope.$apply(); // why?
});
}
});
答案 0 :(得分:3)
$rootScope.$on
注册的回调由$rootScope.$broadcast或$rootScope.$emit触发。如果您探索这些方法源代码,您将看到这些方法本身不会触发$digest
循环(脏检查)。这意味着$digest
应该由调用$broadcast
或$emit
的代码触发,或者在使用$rootScope.$on
注册的回调内触发。
通常情况下,最好假设在$digest
循环内触发回调,这意味着回调调用应该用$apply
包装,如:
$rootScope.$apply($rootScope.$broadcast('event', data));
这与角度最佳做法suggest:
一致$ scope。$ apply()应该尽可能接近async事件绑定 可能的。
答案 1 :(得分:0)
我可以想象如果你想支持来自AngularJS世界之外的事件,$scope.$apply
是必要的。看看如何支持AngularJS(ng-click)和jQuery(onClick)中的事件的差异。
angular.module('app', [])
.controller('ctrl', function($scope) {
$scope.click = function() {
$scope.$emit('inside', {
message: 'from AngularJS world'
})
}
})
.directive('eventListener', function() {
return {
restrict: 'E',
controller: function($scope) {
$scope.message = 'listening'
// in AngularJS context - $apply will throw error that $digest is already running!
$scope.$on('inside', function(event, args) {
$scope.message = args.message
})
// let to know AngularJS that something changes
$scope.$on('outside', function(event, args) {
$scope.$apply(function() {
$scope.message = args.message
}) // this is eqvivalent to:
/*
$scope.message = args.message
$scope.$apply()
*/
})
},
template: '<div>message: {{ message }}</div>'
}
})
.directive('jQueryClick', function() {
return {
restrict: 'E',
link: function(scope, element) {
$(element).on('click', function() {
scope.$emit('outside', {
message: 'outside AngularJS'
})
})
},
template: '<div><button click="click()">jQuery - onClick()!</button></div>',
}
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<div ng-controller="ctrl">
<button ng-click="click()">AngularJS ng-click!</button>
</div>
<div>
<j-query-click></j-query-click>
</div>
<div>
<event-listener></event-listener>
</div>
</div>
由于很难确定变更的来源以及应如何应用于 $ scope ,因此我们可以从 $移动 $ apply 到 $ emit 部分。
angular.module('app', [])
.controller('ctrl', function($scope) {
$scope.click = function() {
$scope.$emit('message', {
message: 'from AngularJS world'
})
}
})
.directive('eventListener', function() {
return {
restrict: 'E',
controller: function($scope) {
$scope.message = 'listening'
/*
* We are goint to support internal and external message in the same way,
* we can simplify support for it
*
$scope.$on('inside', function(event, args) {
$scope.message = args.message
})
$scope.$on('outside', function(event, args) {
$scope.message = args.message
})
*/
$scope.$on('message', function(event, args) {
$scope.message = args.message
})
},
template: '<div>message: {{ message }}</div>'
}
})
.directive('jQueryClick', function() {
return {
restrict: 'E',
link: function(scope, element) {
$(element).on('click', function() {
scope.$apply(scope.$emit('message', {
message: 'outside AngularJS'
}))
})
},
template: '<div><button click="click()">jQuery - onClick()!</button></div>',
}
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<div ng-controller="ctrl">
<button ng-click="click()">AngularJS ng-click!</button>
</div>
<div>
<j-query-click></j-query-click>
</div>
<div>
<event-listener></event-listener>
</div>
</div>