Search code examples
angularjsangularjs-directiveangularjs-scope

AngularJS directive's $scope.var is updated but no class is added. Why? How to fix?


I do not understand what is going on with my $scope.isYellow variable. Why it gets updated just fine but class yellow isn't added to an element?

Here is a jsfiddle: http://jsfiddle.net/akctq645/

The goal is simple: when you click on a "Color" span, it's background has to become yellow. I use ng-class and a custom directive to achieve it. My custom directive's scope has appropriate variable isYellow for that:

<span id="spanElem" make-yellow-directive ng-class="{yellow: isYellow}">Color</span>

controller: function($scope, $element, $attrs, $transclude) {
    $scope.isYellow = 0;
    this.toggleIsYellow = function() {
        $scope.isYellow = ($scope.isYellow == 1) ? 0 : 1;
    }
},

But whatever I do, it doesn't work. For some reason the variable $scope.isYellow in this.toggleIsYellow method gets updated but the span doesn't become yellow, yellow class is not added. Event if I toggle $scope.isYellow manually in Chrome Console debugger, class yellow doesn't appear on span.

Why? What is going on? It is the same scope, it's isYellow variable gets updated but why class yellow isn't added to span?


Solution

  • Angular's two-way binding is powered by the so called digest cycle. In short, Angular keeps its own event queue and uses it to figure out when it needs to walk the scope tree to check for changes. When you use the jQuery 'on()' function, you're creating an event that doesn't go through the Angular digest cycle, meaning that it won't know that it needs to update the ui to match changes in the scope. A quick fix would be to do this:

    function onClickHandler(e) {
        console.log('== onClickHandler ==: ', e);
        
        scope.$apply(function () {
            ctrl.toggleIsYellow(e);
        });
    };
    

    The "$apply" method is used to force Angular to initiate a digest. Though, you should really just use "ng-click" on the span you want to have toggle behavior. Using "$apply" is usually a code-smell in Angular.