3

I'm working on a SPA with AngularJS. After the sign in, a cookie is created with a token and a user object is stored in $rootScope.user which is conveniently accessible from various controllers as they are dynamically loaded.

The application works fine when I use it normally and navigate around.

When I refresh a page with F5, angular is reloaded and the module run() method checks if the cookie exists and reloads the user from the server into $rootScope.user. However, while this is happening, another controller is already expecting $rootScope.user to have a user.

How can I prevent controller initialization before my $rootScope.user is ready and loaded. If can, of course, check in the controller if there is a user loaded in $rootScope. But if there isn't, how can $rootScope invoke a controller to carry out some initialization?

Here is code in app.run():

app.run(function ($rootScope, $location, $cookies, appServices) {

    var token = $cookies.get('UserToken');
    if (token) $rootScope.token = token;

    $rootScope.$on("$routeChangeStart", function (event, next, current) {

        // Get user from server if token is there but no user
        if ($rootScope.user == null) {
            if ($rootScope.token) {
                appServices.getUserByToken(token).then(function (d) {
                    if (d.Code == 1) {
                        $rootScope.user = d.ReturnObj;
                        if ($rootScope.user != null && next.templateUrl == "web/views/signin.html") $location.path("/dashboard");
                    } else $rootScope.goSignin(next.templateUrl);
                });
            }
            else $rootScope.goSignin(next.templateUrl);
        }
    });
})

And here is sample code in a controller that is dynamically loaded:

app.registerCtrl('userdetailCtrl', function userdetailCtrl($scope, $http, $rootScope, $routeParams, appServices, $location, validationService) {

  $scope.getOffices=function()
    {
      appServices.getOffices({ ID: $rootScope.user.OrgID, Token: $rootScope.token }).then(function (d){
      if(d.Code==1){$scope.office = d.ReturnObj;}});
    }

  $scope.getOffices();

});

The getOffices() function requires $rootScope.user.OrgID, which is not found as $rootScope.user is not yet loaded.

Any ideas?

navigator
  • 1,678
  • 16
  • 29

2 Answers2

0

Not sure if this will work in your situation, but

$rootScope.$on('$includeContentLoaded', function () {
    //fetch user here
}

solved a similar issue that I had

li0n_za
  • 455
  • 2
  • 15
0

You can do several things, here's an example of two of these. Check the console in this fiddle

1. Watch the variable for changes:

The first is watching $rootScope.user for changes in your controller:

App.run:

$timeout(function() {
    $rootScope.user = 'user';
}, 500)

in your controller:

$rootScope.$watch('user', function(newVal, oldVal) {
   console.log(newVal, $rootScope.user); 
});

You can 'unwatch', too, if you don't want to keep executing code every time the user variable changes.

2. Emitting an event

The second is emitting an event, and catching it where required, like this:

App.run:

$timeout(function() {
    $rootScope.user = 'user';
    $rootScope.$broadcast('userEvent');
}, 500)

Your controller:

$rootScope.$on('userEvent', function() {
    console.log($rootScope.user);
});
Jorg
  • 7,219
  • 3
  • 44
  • 65
  • Thanks! This looks like a good solution. But I don't want to implement a $watch or $broadcast simply for the rare chance of a browser page refresh. This would mean I would need to write mostly unnecessary code to check for `$rootScope` in many controllers. What I was looking for is a way to halt controller initialization until certain conditions were met. – navigator Sep 24 '15 at 14:37
  • 1
    Your design might need tweaking then. Maybe a user service instead of a rootscope variable, and chain the promises? – Jorg Sep 24 '15 at 20:27
  • Using $timeout to 'resolve' any async issues is really never a good idea. All these should be addressed with "promises" and "resolve". Create a user service (like @jorge said) that returns a promise... Then put the user service in the route.resolve. Then it will wait for the user to resolve. I would advise reading up on "promise", "resolve", "route resolve". These are essential and should be mastered when dealing with async operations. – therebelcoder May 15 '17 at 12:50
  • "write mostly unnecessary code to check" - Uhm? Writing checks is never unnecessary. I think 20% of "our" work consists of doing cool stuff... The other time is spend on writing code to make sure it works in all situations... – therebelcoder May 15 '17 at 12:55