为什么我需要$ rootScope。$ apply在$ rootScope内部。$ on?

时间:2016-11-22 04:51:39

标签: javascript angularjs

为什么此示例中需要$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?
        });
    }
});

2 个答案:

答案 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>