Felix D.
Felix D.

Reputation: 2220

Why I can't access $rootScope value from controller?

I have a variable set to true in the $rootScope. This variable is named loggedIn. I created a NavController to control the display of my main menu links, and as you can see I injected the $rootScope properly.

appControllers.controller("NavController", function($rootScope, $scope) {

  console.log($rootScope); //We can see that loggedIn is true
  console.log($rootScope.loggedIn); //Outputs false 

  //Not working as expected
  if($rootScope.loggedIn === true){
    $scope.showHomeMenu = false;
    $scope.showGameMenu = true;
    $scope.currentHome = '/lobby';
  }
  else {
    $scope.showHomeMenu = true;
    $scope.showGameMenu = false;
    $scope.currentHome = '/login';
  }
});

Oddly, this doesn't work because $rootScope.loggedIn is evaluated to false, even though it's value was set to true. This is the output I get from the console.

Console output

As you can see from the $rootScope output, loggedIn should be true. But it's evaluated to false in the controller.

I guess I'm doing something really dumb here, but I can't figure out why!!

Edit 2

@rvignacio pointed out something interesting. The object $rootScope I see is the state of the object at the time of the expansion, not the state at the moment when I call log(). (which was predictable...)

I guess the issue is deeper than I thought! I'll have to dig to find the bug!

Edit

I set loggedIn in run().

app.config(function($routeProvider, $httpProvider) {
  $routeProvider.
  when('/', {
   ...
  }).
   ...
  });


  var interceptor = ['$rootScope', '$q',
    function(scope, $q) {

      function success(response) {
        return response;
      }

      function error(response) {
        var status = response.status;

        if (status == 401) {
          var deferred = $q.defer();
          var req = {
            config: response.config,
            deferred: deferred
          };
          scope.$broadcast('event:loginRequired');
          return deferred.promise;
        }
        // otherwise
        return $q.reject(response);

      }

      return function(promise) {
        return promise.then(success, error);
      };

    }
  ];
  $httpProvider.responseInterceptors.push(interceptor);
}).run(['$rootScope', '$http', '$location',
  function(scope, $http, $location) {

    /**
     * Holds page you were on when 401 occured.
     * This is good because, for example:
     *  User goes to protected content page, for example in a bookmark.
     *  401 triggers relog, this will send them back where they wanted to go in the first place.
     */
    scope.pageWhen401 = "";
    scope.loggedIn = false;

    scope.logout = function() {
      $http.get('backend/logout.php').success(function(data) {
        scope.loggedIn = false;
        scope.$broadcast('event:doCheckLogin');
      }).error(function(data) {
        scope.$broadcast('event:doCheckLogin');
      });
    };

    scope.$on('event:loginRequired', function() {    
      //Only redirect if we aren't on restricted pages
      if ($location.path() == "/signup" ||
        $location.path() == "/login" ||
        $location.path() == "/contact" ||
        $location.path() == "/about")
        return;

      //go to the login page
      $location.path('/login').replace();
    });

    /**
     * On 'event:loginConfirmed', return to the page.
     */
    scope.$on('event:loginConfirmed', function() {
      //*********************
      //THIS IS WHERE I SET loggedIN
      //*********************
      scope.loggedIn = true;
      console.log("Login confirmed!");
      $location.path('/lobby').replace();
    });

    /**
     * On 'logoutRequest' invoke logout on the server and broadcast 'event:loginRequired'.
     */
    scope.$on('event:logoutRequest', function() {
      scope.logout();
    });

    scope.$on("$locationChangeSuccess", function(event) {
      //event.preventDefault();
      ping();
    });

    scope.$on('event:doCheckLogin', function() {
      ping();
    });

    /**
     * Ping server to figure out if user is already logged in.
     */
    function ping() {
      $http.get('backend/checksession.php').success(function() {
        scope.$broadcast('event:loginConfirmed');
      }); //If it fails the interceptor will automatically redirect you.
    }

    //Finally check the logged in state on every load
    ping();
  }
]);

Upvotes: 0

Views: 1678

Answers (1)

Felix D.
Felix D.

Reputation: 2220

You guys were right. I'm using async calls to set loggedIn to true, so it was all a question of timing.

Welcome to the world of aynschronism dear me.

$watch-ing loggedIn solved the issue.

appControllers.controller("NavController", function($rootScope, $scope) {
    $scope.showHomeMenu = true;
    $scope.showGameMenu = false;
    $scope.currentHome = '/login';

  //ADDED
  $rootScope.$watch('loggedIn', function(){
  console.log($rootScope.loggedIn);  
  if($rootScope.loggedIn === true){
    $scope.showHomeMenu = false;
    $scope.showGameMenu = true;
    $scope.currentHome = '/lobby';
  }
  else {
    $scope.showHomeMenu = true;
    $scope.showGameMenu = false;
    $scope.currentHome = '/login';
  }});
});

Also I changed where I set loggedIn to false in my code to increase performance and remove visible latency when logging out. Now everything runs fast.

Upvotes: 1

Related Questions