Search code examples
javascriptangularjsjqlite

Is calling Angular's $apply necessary after clicking bound directive to update controller $scope?


Snippets below, Plunker here: http://plnkr.co/edit/WA6tIfqXV6dO4pbsqxu9?p=preview

A challenge I'm often facing is updating the controller scope from the directive scope upon firing a binding on the directive element, e.g. 'click'. I've got it working by calling $apply() after changing the directive's scope, but is that overkill?

So say I have a controller value aptly named "value":

  .controller( 'coolCtrl', [ '$scope', function( $scope )
  {
    $scope.value = 1;
  }])

And I want to increase that value from within a directive upon clicking the directive element:

  .directive( 'testScope', function()
  {
    return {
      restrict: 'E',
      replace: true,
      scope: {
        value: '='
      },
      template: '<button>Increase value</button>',
      link: function( scope, element, attrs )
      {
        // Note: just using standard jqLite here
        element.on( 'click', function()
        {
          scope.value++;
          scope.$apply();
        });
      }
    };
  })

Is calling an $apply necessary to update the parent scope on every click? What if the user were typing, would the entire parent scope downwards have to go through a $digest cycle on each keydown?

PS my familiarity with Angular is humbling.


Solution

  • If you are using the native browser events you have to use $apply to make the changes visible to angular. And yes, if you are using keydown a complete digest cycle will happen. But in most cases this is not a problem.

    You may really reduce your code if you are using the build in ng-* directives. For example you may use ng-click for your problem:

    .directive( 'testScope', function()
      {
        return {
          restrict: 'E',
          replace: true,
          scope: {
            value: '='
          },
          controller: function($scope){
              $scope.increase = function(){
                $scope.value++;
              }
          },
          template: '<button ng-click="increase()">Increase value</button>'
        }; 
      })
    

    If you go this way, there is no need for calling $apply manually. This is the angular way...