Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Service variable not updating in controller

Tags:

I have a NavbarCtrl that is outside of ng-view. I have a login controller that talks to a service to get a user logged in. Once the user is logged in, I want the Navbar to update with the user's email address. However for the life of me, I can't seem to get the Navbar scope to update with the data that is loaded in to my "Auth" service once the user is logged in.

This is my main index.html:

<div class="navbar navbar-inverse navbar-fixed-top">          <div class="navbar-inner">             <div class="container">                 <a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">                     <span class="icon-bar"></span>                     <span class="icon-bar"></span>                     <span class="icon-bar"></span>                 </a>                 <a class="brand" href="#">Brim</a>                  <div class="pull-right"  ng-controller="NavbarCtrl">                     <div ng-click="refresh()">hello</div>                     {{ user.email }}                 </div>             </div>         </div>      </div>      <div class="container" ng-view> 

And my service:

.factory('Auth', function($resource) {     var authenticated = false;     var user = {};     return {         isAuthenticated: function () {             return authenticated;         },         getUser: function() {             return user;         },         login: function(loginUser, callback) {             user =  {email:'[email protected]'}             authenticated = true;             callback(true);             //actual code for logging in taken out for brevity         }     } }) 

And my Login and Navbar controllers:

function LoginCtrl($scope, $location, Auth) {  $scope.login = function() {     Auth.login($scope.user, function(success) {         if(success) $location.path('/dashboard');         //console.log(Auth.getUser())     }); }  }  function NavbarCtrl($scope, Auth)  {  //thought this should work $scope.user = Auth.getUser();  //experimenting unsuccessfully with $watch $scope.$watch(Auth.isAuthenticated(),function () {     $scope.user = Auth.getUser() })  //clicking on a refresh button is the only way I can get this to work $scope.refresh = function() {     $scope.user = Auth.getUser() } } 

From my research I would have thought that $scope.user = Auth.getUser(); would work, but it's not and I'm at a complete loss as to how how I can get my Navbar updated when a user is logged in. Thanks in advance for any help.

like image 212
Karl Campbell Avatar asked Mar 13 '13 08:03

Karl Campbell


1 Answers

Update: well, you learn something new every day... just remove the () to watch the results of a function:

$scope.$watch(Auth.isAuthenticated, function() { ... }); 

Updated fiddle

In this fiddle, notice how 'watch1' and 'watch3' both trigger a second time when the value of $scope.isAuthenticated changes.

So, this is the general technique to watch for changes to a primitive value that is defined on a service:

  • define an API/method that returns (the value of) the primitive
  • $watch that method

To watch for changes to an object or array that is defined on a service:

  • define an API/method that returns (a reference to) the object/array
  • In the service, be careful to only modify the object/array, don't reassign it.
    E.g., don't do this: user = ...;
    Rather, do this: angular.copy(newInfo, user) or this: user.email = ...
  • normally you'll assign a local $scope property the result of that method, hence the $scope property will be a reference to the actual object/array
  • $watch the scope property

Example:

$scope.user = Auth.getUser(); // Because user is a reference to an object, if you change the object // in the service (i.e., you do not reassign it), $scope.user will // likewise change. $scope.$watch('user', function(newValue) { ... }, true); // Above, the 3rd parameter to $watch is set to 'true' to compare // equality rather than reference.  If you are using Angular 1.2+ // use $watchCollection() instead: $scope.$watchCollection('user', function(newValue) { ... }); 

Original answer:

To watch the results of a function, you need to pass $watch a function that wraps that function:

$scope.$watch( function() { return Auth.isAuthenticated() }, function() { ... }); 

Fiddle. In the fiddle, notice how only 'watch3' triggers a second time when the value of $scope.isAuthenticated changes. (They all trigger initially, as part of $watch initialization.)

like image 158
Mark Rajcok Avatar answered Sep 22 '22 16:09

Mark Rajcok