yarek
yarek

Reputation: 12034

angularjs: how to watch a global variable change?

I want to watch the myApp.myUser changes:

<html ng-app="myApp">
<head>
<script src="js/vendor/angular/angular.min.js"></script>
  <script src="js/app2.js"></script>
</head>
<body>
    <div id="users" ng-controller="usersController">
    <h4>usersController</h4>
    test = {{myUser}}
    </div>        
    <div id="tasks" ng-controller="tasksController">
    <button ng-click="clickMe()">cliquez ici </button>
    </div>
</body>
</html>

and app2.js:

var myApp = angular.module('myApp', []);
myApp.users = {};
myApp.tasks = {};
myApp.myUser = {id:1, username:'john'}

myApp.controller('usersController', function($scope) {
    $scope.myUser = myApp.myUser;
    $scope.$watch(myApp.myUser, $scope.userChanged);
    $scope.userChanged = function() {
        console.log('userChanged');
    }   
});

myApp.controller('tasksController', function($scope) {
    $scope.tasks = myApp.tasks;
    $scope.clickMe = function() {
        myApp.myUser = {id:2, username:'jack'}
    }
});

I tried that with console:

myApp.myUser = {id:2, username:'jack'}

Nothing. Then I tried to pass that through the AngularJS with tasksController

$scope.clickMe = function() {
    myApp.myUser = {id:2, username:'jack'}
}

Nothing changes on my view!

http://jsfiddle.net/halirgb/Lvc0u55v/

Upvotes: 2

Views: 4159

Answers (3)

rmhunter
rmhunter

Reputation: 581

Don't use properties on your myApp variable, that literally holds all of the AngularJS functionality, and you risk collisions.

You have a couple options for storing data in AngularJS:

  1. $rootScope - Operates just like $scope, but is attached to the whole app, not just the current controller.
  2. Services. This is the preferred method, because it keeps your code modular and reusable. In your case, you could set up a data service.

That would look something like...

myApp.factory('dataService', function() {
    // this is simplified for illustration, see edit below
    return {
        myUser: {id: 1, username: 'john'},
    };
});

myApp.controller('usersController', function($scope, dataService) {
    $scope.myUser = dataService.myUser;
});

EDIT: As discussed in comments, this brings up another potential binding issue, since I used a literal value that can be overwritten via userService.myUser = {};. A better data service, which would provide more reliable 2-way binding, would look like:

myApp.factory('dataService', function() {
    var users = [
        { id: 1, username: 'john' }
    ];

    return {
        getUser: function(id) {
            for (i=0; i<users.length; i++) {
                if (users[i].id === id) {
                    return users[i];
                }
            }
        }
    };
});

myApp.controller('usersController', function($scope, dataService) {
    $scope.myUser = dataService.getUser(1);
});

Upvotes: 2

Aravind
Aravind

Reputation: 41543

I would suggest you to use the variables by declaring it in a service as

var myApp = angular.module('myApp', []);
    myApp.factory('DataService',function(){
      var myUser ={id:1, username:'john'};
      var tasks;
      var users;
      return{
      users:users,
      tasks:tasks,
      myUser:myUser
      }
    });
    myApp.controller('usersController', function($scope,DataService) {
        $scope.myUser = DataService.myUser;
        console.log(DataService.myUser);   
       $scope.$watch(DataService.myUser, $scope.userChanged);
        $scope.userChanged = function() {
            console.log('userChanged');
        }   
    });

    myApp.controller('tasksController', function($scope,DataService) {
        $scope.tasks = DataService.tasks;
        $scope.clickMe = function() {
            DataService.myUser = {id:2, username:'jack'}
        }
    });

Upvotes: 2

Jesse Amano
Jesse Amano

Reputation: 828

As shown in the docs for $scope.$watch, the first argument must be a watchExpression - that is, an Angular expression (a string) describing what value in $scope should be watched.

Your value myApp.myUser is not part of $scope and cannot be reached by $watch. The solution would be to move your object into something like $scope.myUser and then call $scope.$watch('myUser', function() { console.log('userChanged'); });.

Be warned that:

  • $watch can be expensive if you set up a lot of watches. You usually want a watch set up for data that your users can manipulate on the view, but not for data that you're directly loading through code.
  • If the expression points to an object, $watch will only track changes to the reference, so $scope.myUser = {}; would trigger a listener callback but $scope.myUser.username = 'jack'; would not. You can pass a third argument true to make it check for deep inequality, but this gets even more expensive with complex objects or lots of watches.

Upvotes: 1

Related Questions