2

I've created some directives which have some functions, something like this:

myCtrl.directive('myDirective', function () {
    return {
        restrict: 'E',
        scope: {
            'something': '='
        },
        link: function (scope, el, attr) { 
             function func1(){
                 //some stuff
                 scope.$apply();
             }
             function func2(){
                 //some stuff
                 scope.$apply();
             }
             function func3(){
                 //some stuff
                 scope.$apply();
             }
             //...
        }
    }
});

I have to call scope.$apply() in all the functions to update the view. In addition I don't want to define them in the controller. I don't know if there is a trick to avoid this kind of pattern. It is working but I think it's not a good solution.

UPDATE

There are more than 10 directives which we've created for some shapes, e.g. rectangle, circle, square etc. In these functions, which I called $apply in them, some methods like drag and scale are implemented. So I need to call $apply to modify changes in model and consequently the view will be updated. I don't know how can I make them able to aware scope automatically unless I write about 100 functions in a controller! In addition, I'm using some 3rd party libraries like d3.js. Some event like clicked are bounded for calling these functions.

Saman Gholami
  • 3,416
  • 7
  • 30
  • 71

4 Answers4

3

The real truth is that you need to call $scope.$apply() whenever you're changing something outside angular digest cycle. Angular detects the changes by dirty checking during the digest cycle.

$timeout is also doing $apply ($rootScope.$apply())

Check Angular source code.

What I can suggest is create helper function which will handle that. Example:

var ngAware = function (fnCallback) {
    return function () {
        var result = fnCallback.apply(this, arguments);    
        $scope.$apply();
        return result;
    };   
};   

// usage
function func1 () {
    // impl...
}
var ngFunc1 = ngAware(func1);
// or
var func2 = ngAware(function func2 () {
    // impl...
});
Community
  • 1
  • 1
S.Klechkovski
  • 4,005
  • 16
  • 27
0

Sure you need apply()?

apply() is used when integrating 3rd party libraries (such as jQuery). In most cases, if you are not hooking up 3rd party javascript libraries, you can avoid $apply() by using the build in binding mechanism

For instance, the following snippet will call func1 using a 3 seconds timeout callback. then, func1 will manipulate the scope variable something and the view will be updated without the need to trigger $apply()

var app = angular.module('plunker', []);

app.controller('MainCtrl', function($scope) {
  $scope.value = 'Hello';
});


app.directive('myDirective', function ($timeout) {
    return {
        restrict: 'E',
        template:'<h1>{{something}}</h1>',
        scope: {
            'something': '='
        },
        link: function (scope, el, attr) { 
             function func1(){
                scope.something += ' ' + scope.something;
             }
             
             $timeout(func1, 3000);
        }
    }
});

html

<body ng-controller="MainCtrl">
   <my-directive something="value"></my-directive>
</body>

http://plnkr.co/edit/42VYs0W2BC5mjpaVWQg3?p=preview

  • perhaps if you explain more about your use case i could expand my answer to your specific needs
Community
  • 1
  • 1
Jossef Harush Kadouri
  • 32,361
  • 10
  • 130
  • 129
  • 1
    Using $apply in directives isn't that hacky as its needed for interfacing anything outside of angular (so DOM and browser events too). Many of the ng directives use $apply. – Tahsis Claus Sep 03 '15 at 20:43
0

wraps the function within $scope.$apply()

check this :

var app = angular.module('plunker', []);

app.controller('MainCtrl', function($scope) {
    $scope.value = 'Hello';
});


app.directive('myDirective', function ($timeout) {
    return {
        restrict: 'E',
        template:'<h1>{{something}}</h1>',
        scope: {
            'something': '='
        },
        link: function (scope, el, attr) { 
            scope.$apply(function(){

                function func1(){
                    //some stuff
                }

                function func2(){
                    //some stuff
                }

                function func3(){
                    //some stuff
                }
            })
        }
    }
});
gaurav bhavsar
  • 2,033
  • 2
  • 22
  • 36
0

If you have common functionality in you directives (circle, square, etc) you should group it. By example, we could have a directive to drag

.directive('ngDraggable', [function () {

    return {
        link: function ($scope) {

            var stopHandler = function () {

                $scope.apply(function () {
                    // Code to handler the end of the dag event

                    // Notify the other directive.
                    $scope.$emit('dragStopped', data);
                });
            };

            // Initialization of the drag functionality.
        }
    }
}])

You can used as

<div ng-circle ng-draggable></div>

So, you earn three thing. First, the circle directive can do changes in its scope without call to $apply, because $emit already is in the digest cycle. Second, you only use $apply where is necessary. And third, you improve the performance by use $apply in scopes most small and with less children.

You could merge this answer with the answer of @sasko_dh

Maybe the Observer Pattern could be you useful.

Victor Aguilar
  • 455
  • 4
  • 18