最新消息:Welcome to the puzzle paradise for programmers! Here, a well-designed puzzle awaits you. From code logic puzzles to algorithmic challenges, each level is closely centered on the programmer's expertise and skills. Whether you're a novice programmer or an experienced tech guru, you'll find your own challenges on this site. In the process of solving puzzles, you can not only exercise your thinking skills, but also deepen your understanding and application of programming knowledge. Come to start this puzzle journey full of wisdom and challenges, with many programmers to compete with each other and show your programming wisdom! Translated with DeepL.com (free version)

javascript - Waiting for $rootScope value to resolve in Angular before page load - Stack Overflow

matteradmin5PV0评论

So I'm running into this problem where I'm using ngView and I have a navigation bar that is static throughout like so:

<div ng-include="'views/nav.html'" ng-controller="NavCtrl"></div>
<div class="container-fluid" ng-view=""></div>

This nav.html, the navigation bar, displays a certain set of functions (Login, Register) if the user is logged out (using ng-show) and other menu options if the user is logged in. Because of the heavy use of current user, I've put this information in the $rootScope like this: $rootScope.currentUser - returns user object, and $rootScope.signedIn - return boolean.

Basically, I want to delay the navbar from loading until $rootScope.signedIn is loaded and either true or false, and $rootScope.currentUser is an object or undefined.

I've tried messing around with creating promises in my app.config routes, but I'm not sure how I can return a promise to the permanent view state.

Any help is appreciated.

Edit:

Here is the service in which I broadcast my login. This fires anytime a user is authenticated/logged in or anytime they logout:

    var authClient = new FirebaseSimpleLogin(refDownload, function(error, user) {
        if (error) {
            incorrectLogin(error.code);
        }
        if (user) {
            // user authenticated
            $rootScope.$broadcast('login');
            correctLogin(user.id);
        } else {
            // user is logged out
            $rootScope.$broadcast('logout');
        }
    });

This service is injected into the NavCtrl controller in the following way:

    $scope.isHidden = true;

    $scope.$on('login', function() {
        console.log('login broadcast');
        $scope.isHidden = false;
    });

    $scope.$on('logout', function() {
        console.log('broadcast logout');
        $scope.isHidden = true;
    });

The template for this controller is nav.html that looks like this:

<div class="col-xs-4 centered" id="nav-hover"  ng-show="isHidden">
    <ul class="nav navbar-nav">
        <li id="nav-login"><a ng-href="#/login"><span class="glyphicon glyphicon-log-in">&nbsp;Login</span></a></li>
    </ul>
</div>

<div class="col-xs-4 centered" id="nav-hover" ng-show="isHidden">
    <ul class="nav navbar-nav">
        <li id="nav-login"><a ng-href="#/register"><span class="glyphicon glyphicon-edit">&nbsp;Register</span></a></li>
    </ul>
</div>


<div class="col-xs-2 centered" id="nav-hover">
    <ul class="nav navbar-nav" ng-hide="isHidden">
        <li ng-class="{{ chatCat.active }}"><a ng-href="{{ chatCat.url }}"><span class="{{ chatCat.icon }}"></span></a></li>
    </ul>
</div>

Again, this view is bound to NavCtrl. When logging users in, I use AuthCtrl as follows:

    $scope.login = function() {
        if ($scope.user !== undefined) {
            Auth.login($scope.user);
            $location.path('/dexter');
        } else {
            console.log('nothing entered');
        }               
    };

When I try to login, the nav view does not update with the new values, although the broadcast is fired from the service with 'logged in'.

Auth service:

'use strict';

app.factory('Auth',
    function($rootScope, $location, $firebase, $firebaseSimpleLogin, firebaseUrl) {

    var refDownload = new Firebase(firebaseUrl + 'housemates');

    var sync = $firebase(refDownload); 

    var ref = sync.$asObject();

    var authClient = new FirebaseSimpleLogin(refDownload, function(error, user) {
        if (error) {
            incorrectLogin(error.code);
        }
        if (user) {
            // 1
            // user authenticated
            correctLogin(user.id);
        } else {
            // user is logged out
            // $rootScope.signedIn = false;
        }
    });

    var Auth = {

        housemates: ref,

        changeColor: function(color) {
            var id = $rootScope.currentUser.id.toString();
            refDownload.child(id).update({ color: color });
            $rootScope.currentUser.color = color;
        },


        create: function(authUser, usr) {
            refDownload.child(authUser.id).set({
                initials: usr.initials,
                email: authUser.email,
                password: usr.password,
                color: 'Blue',
                id: authUser.id,
                uid: authUser.uid,
                rememberMe: true,
            });

        },

        // 3
        findById: function(id) {
            refDownload.on('value', function(snapshot) {
                var userObject = snapshot.val();
                // 4 - sets currentUser
                //$rootScope.currentUser = userObject[id];
                var currentUser = userObject[id];
                Auth.setUser(currentUser);
                // $rootScope.signedIn = true;
            }, function (error) {
                console.log(error);
            });
        },

        login: function(user) {
            authClient.login('password', {
                email: user.email,
                password: user.password,
                rememberMe: true
            });
        },

        logout: function() {
            delete $rootScope.currentUser;
            delete $rootScope.signedIn;
            delete $rootScope.error;
            return authClient.logout();
        },

        register: function(user) {
            var userSimple = user;
            authClient.createUser(user.email, user.password, function(error, user) {
                if(!error) {
                    var userComplex = user;
                    Auth.login(userSimple);
                    Auth.create(userComplex, userSimple);
                    return user;
                } else {
                    console.log(error);
                }
            });

        },

        setUser: function(aUser) {
            console.log('setuser ran');
            $rootScope.currentUser = aUser;
            console.log('setUser: ' + $rootScope.currentUser);
        },

        isLoggedIn: function() {
            console.log($rootScope.currentUser);
            return ($rootScope.currentUser) ? $rootScope.currentUser : false;
        },


    };

    // 2
    function correctLogin(id) {
        Auth.findById(id);
    }

    function incorrectLogin(error) {
        alert(error);
        $rootScope.error = error;
    }

    return Auth;


});

So I'm running into this problem where I'm using ngView and I have a navigation bar that is static throughout like so:

<div ng-include="'views/nav.html'" ng-controller="NavCtrl"></div>
<div class="container-fluid" ng-view=""></div>

This nav.html, the navigation bar, displays a certain set of functions (Login, Register) if the user is logged out (using ng-show) and other menu options if the user is logged in. Because of the heavy use of current user, I've put this information in the $rootScope like this: $rootScope.currentUser - returns user object, and $rootScope.signedIn - return boolean.

Basically, I want to delay the navbar from loading until $rootScope.signedIn is loaded and either true or false, and $rootScope.currentUser is an object or undefined.

I've tried messing around with creating promises in my app.config routes, but I'm not sure how I can return a promise to the permanent view state.

Any help is appreciated.

Edit:

Here is the service in which I broadcast my login. This fires anytime a user is authenticated/logged in or anytime they logout:

    var authClient = new FirebaseSimpleLogin(refDownload, function(error, user) {
        if (error) {
            incorrectLogin(error.code);
        }
        if (user) {
            // user authenticated
            $rootScope.$broadcast('login');
            correctLogin(user.id);
        } else {
            // user is logged out
            $rootScope.$broadcast('logout');
        }
    });

This service is injected into the NavCtrl controller in the following way:

    $scope.isHidden = true;

    $scope.$on('login', function() {
        console.log('login broadcast');
        $scope.isHidden = false;
    });

    $scope.$on('logout', function() {
        console.log('broadcast logout');
        $scope.isHidden = true;
    });

The template for this controller is nav.html that looks like this:

<div class="col-xs-4 centered" id="nav-hover"  ng-show="isHidden">
    <ul class="nav navbar-nav">
        <li id="nav-login"><a ng-href="#/login"><span class="glyphicon glyphicon-log-in">&nbsp;Login</span></a></li>
    </ul>
</div>

<div class="col-xs-4 centered" id="nav-hover" ng-show="isHidden">
    <ul class="nav navbar-nav">
        <li id="nav-login"><a ng-href="#/register"><span class="glyphicon glyphicon-edit">&nbsp;Register</span></a></li>
    </ul>
</div>


<div class="col-xs-2 centered" id="nav-hover">
    <ul class="nav navbar-nav" ng-hide="isHidden">
        <li ng-class="{{ chatCat.active }}"><a ng-href="{{ chatCat.url }}"><span class="{{ chatCat.icon }}"></span></a></li>
    </ul>
</div>

Again, this view is bound to NavCtrl. When logging users in, I use AuthCtrl as follows:

    $scope.login = function() {
        if ($scope.user !== undefined) {
            Auth.login($scope.user);
            $location.path('/dexter');
        } else {
            console.log('nothing entered');
        }               
    };

When I try to login, the nav view does not update with the new values, although the broadcast is fired from the service with 'logged in'.

Auth service:

'use strict';

app.factory('Auth',
    function($rootScope, $location, $firebase, $firebaseSimpleLogin, firebaseUrl) {

    var refDownload = new Firebase(firebaseUrl + 'housemates');

    var sync = $firebase(refDownload); 

    var ref = sync.$asObject();

    var authClient = new FirebaseSimpleLogin(refDownload, function(error, user) {
        if (error) {
            incorrectLogin(error.code);
        }
        if (user) {
            // 1
            // user authenticated
            correctLogin(user.id);
        } else {
            // user is logged out
            // $rootScope.signedIn = false;
        }
    });

    var Auth = {

        housemates: ref,

        changeColor: function(color) {
            var id = $rootScope.currentUser.id.toString();
            refDownload.child(id).update({ color: color });
            $rootScope.currentUser.color = color;
        },


        create: function(authUser, usr) {
            refDownload.child(authUser.id).set({
                initials: usr.initials,
                email: authUser.email,
                password: usr.password,
                color: 'Blue',
                id: authUser.id,
                uid: authUser.uid,
                rememberMe: true,
            });

        },

        // 3
        findById: function(id) {
            refDownload.on('value', function(snapshot) {
                var userObject = snapshot.val();
                // 4 - sets currentUser
                //$rootScope.currentUser = userObject[id];
                var currentUser = userObject[id];
                Auth.setUser(currentUser);
                // $rootScope.signedIn = true;
            }, function (error) {
                console.log(error);
            });
        },

        login: function(user) {
            authClient.login('password', {
                email: user.email,
                password: user.password,
                rememberMe: true
            });
        },

        logout: function() {
            delete $rootScope.currentUser;
            delete $rootScope.signedIn;
            delete $rootScope.error;
            return authClient.logout();
        },

        register: function(user) {
            var userSimple = user;
            authClient.createUser(user.email, user.password, function(error, user) {
                if(!error) {
                    var userComplex = user;
                    Auth.login(userSimple);
                    Auth.create(userComplex, userSimple);
                    return user;
                } else {
                    console.log(error);
                }
            });

        },

        setUser: function(aUser) {
            console.log('setuser ran');
            $rootScope.currentUser = aUser;
            console.log('setUser: ' + $rootScope.currentUser);
        },

        isLoggedIn: function() {
            console.log($rootScope.currentUser);
            return ($rootScope.currentUser) ? $rootScope.currentUser : false;
        },


    };

    // 2
    function correctLogin(id) {
        Auth.findById(id);
    }

    function incorrectLogin(error) {
        alert(error);
        $rootScope.error = error;
    }

    return Auth;


});
Share Improve this question edited Dec 9, 2015 at 3:07 Himmel asked Oct 3, 2014 at 14:07 HimmelHimmel 3,7097 gold badges43 silver badges78 bronze badges 12
  • Maybe the ngCloak could help you. Or a $timeout maybe... – Denis C de Azevedo Commented Oct 3, 2014 at 14:11
  • I tried putting ng-cloak on the top div in the nav.html view, but it doesn't fix the problem. – Himmel Commented Oct 3, 2014 at 14:14
  • 1 You should also try a service instead of $rootScope. – Sergiu Paraschiv Commented Oct 3, 2014 at 14:15
  • I started out using a service for my current user details, but I'm calling my user object information on literally every page and on the views - isn't that use case for $rootScope? Also, this doesn't address my problem. – Himmel Commented Oct 3, 2014 at 14:18
  • There's no use case for $rootScope except broadcasts maybe, and you should avoid those too. A service is persistent (sort of a singleton) so there's no harm in $scope.foo = someService.getSomeData();. – Sergiu Paraschiv Commented Oct 3, 2014 at 14:25
 |  Show 7 more ments

3 Answers 3

Reset to default 7

With a bit of $rootScope.$broadcast and ng-hide on the menu, this could be easily acplished. See this plunker

The html:

<!DOCTYPE html>
<html ng-app="plunker">

  <head>
    <meta charset="utf-8" />
    <title>AngularJS Plunker</title>
    <script>document.write('<base href="' + document.location + '" />');</script>
    <link rel="stylesheet" href="style.css" />
    <script data-require="[email protected]" src="https://code.angularjs/1.2.25/angular.js" data-semver="1.2.25"></script>
    <script src="app.js"></script>
  </head>

  <body ng-controller="MainCtrl">
  <div ng-include="'nav.html'" ng-controller="NavCtrl" ng-hide="isHidden"></div>
  <button class="btn" ng-click="login()">Login</button>
  <button class="btn" ng-click="logout()">Logout</button>
  </body>

</html>

The javascript:

var app = angular.module('plunker', []);

app.controller('MainCtrl', function($scope, $rootScope) {
  $scope.login = function() {
    $rootScope.$broadcast("login");
  }

  $scope.logout = function() {
    $rootScope.$broadcast("logout");
  }
});

app.controller('NavCtrl', function($scope) {
  $scope.isHidden = true;
  $scope.$on('login', function() {
    console.log("logged in");
    $scope.isHidden = false;
  });

  $scope.$on('logout', function() {
    console.log("logged out");
    $scope.isHidden = true;
  });
});

OK, if the way i suggested isn't working for you, here is a second possible solution (plunker)

The base idea is to have a service (in this case a factory) in which you set the logged in user name and then in the nav controller use $watch to watch changes to the authentication status in the service. And the code:

<!DOCTYPE html>
<html ng-app="plunker">

  <head>
    <meta charset="utf-8" />
    <title>AngularJS Plunker</title>
    <script>document.write('<base href="' + document.location + '" />');</script>
    <link rel="stylesheet" href="style.css" />
    <script data-require="[email protected]" src="https://code.angularjs/1.2.25/angular.js" data-semver="1.2.25"></script>
    <script src="app.js"></script>
    <script src="Auth.js"></script>
  </head>

  <body ng-controller="MainCtrl">
  <div ng-include="'nav.html'" ng-controller="NavCtrl" ng-hide="isHidden"></div>
  <button class="btn" ng-click="login()">Login</button>
  <button class="btn" ng-click="logout()">Logout</button>
  </body>

</html>

The javascript:

var app = angular.module('plunker', []);

app.controller('MainCtrl', function($scope, $rootScope, Auth) {
  $scope.login = function() {
    var user = "iris"
    Auth.setUser(user);
  }

  $scope.logout = function() {
    Auth.setUser(null);
  }
});

app.controller('NavCtrl', function($scope, Auth) {
  $scope.isHidden = true;

  $scope.$watch(Auth.isLoggedIn, function (value, oldValue) {

    console.log("authentication changed");

    if(!value && oldValue) {
      console.log("logged out");
      $scope.isHidden = true;
    }

    if(value) {
      console.log("logged in");
      $scope.isHidden = false;
    }

  }, true);


});

and the service:

app.factory('Auth', function() {
  var user;

  return {
    setUser: function(aUser) {
      user = aUser;
    },
    isLoggedIn: function() {
      console.log(user);
      return (user) ? user : false;
    }
  }
})

@zszep $broadcast answer solved the problem with one caveat. It was necessary to add $scope.$apply() following each of the $scope.isHidden mands in NavCtrl. This forced a page refresh of sorts and the Nav view updated.

Post a comment

comment list (0)

  1. No comments so far