Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free.

I am getting started with AngularJS and have a noob problem that I am not sure how to resolve. I am modifying a value outside of angular (I have put it in the .run section only for demonstration purposes), and then attempting to run $apply so that Angular will notice that the scope needs to be updated.

However, in the following code, the {{currentState}} value gets set to "Initial value" and does not ever update to "Second value".

What is the correct approach to get the value to update?

angular.module("exampleApp", [])
.run(function(userNotificationService) {
    userNotificationService.setStatus("Initial value");
    setTimeout(function() {
       userNotificationService.setStatus("Second value");
    }, 1000);
})
.factory('userNotificationService', function($rootScope) {
   var currentState = 'Unknown state'; // this should never be displayed
   return {
     setStatus: function(state) {
        $rootScope.$apply(function() {
            currentState = state;
        });
     },
     getStatus: function() {
        return currentState;
     }
  };
}).directive('currentState', function(userNotificationService) {
    return {
        restrict: 'AE',
        scope: false, // set to false so that directive scope is used for transcluded expressions
        link: function(scope) {
            scope.currentState = userNotificationService.getStatus();
        }
    };
}).controller("defaultCtrl", function ($scope) {
// does nothing
});

And the html is the following:

<body ng-controller="defaultCtrl">
    <div current-state>
        current state: {{ currentState }}
    </div>
</body>
share|improve this question

1 Answer 1

up vote 2 down vote accepted

If your use-case involves a timer, then Angular provides its own timer service called $interval which wraps the call in a scope.$apply for you. You should use that instead of setTimeout.

Now in this case, since you need a one way binding between a service and a value in your scope, you can set up a $watch in your directive:

 .directive('currentState', function(userNotificationService) {
    return {
        restrict: 'AE',
        scope: false, // set to false so that directive scope is used for transcluded expressions
        link: function(scope) {
            scope.$watch(function () { return userNotificationService.getStatus(); }, function (newVal) {
                scope.currentState = userNotificationService.getStatus();
             });
        }
    };

Ideally how you would do it is by creating this one way (or two way) binding in your controller (which you have left empty). The $scope you define on the controller will be available to the directive (if you set $scope: false or $scope: true), and then you can leave the link function empty.

share|improve this answer
    
Thank you! Your suggestion worked perfectly. –  Alexander Marquardt Jul 10 '14 at 21:30
    
scope.currentState = newVal works too, as opposed to calling userNotificationService.getStatus() again. –  Alexander Marquardt Jul 10 '14 at 21:57
    
@AlexanderMarquardt It depends. On the first execution, the newVal may be undefined if the first argument to $watch is a string. If the operation is expensive, then it may be better to use newVal and guard against undefined-edness. –  musically_ut Jul 11 '14 at 6:07

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.