【问题标题】:Waiting for $rootScope value to resolve in Angular before page load在页面加载之前等待 $rootScope 值在 Angular 中解析
【发布时间】:2014-10-03 14:07:46
【问题描述】:

所以我在使用 ngView 时遇到了这个问题,并且我有一个始终是静态的导航栏,如下所示:

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

这个nav.html,导航栏,如果用户退出(使用ng-show)显示一组特定的功能(Login,Register),如果用户登录则显示其他菜单选项。使用当前用户,我将这些信息放在 $rootScope 中,如下所示:$rootScope.currentUser - 返回用户对象,$rootScope.signedIn - 返回布尔值。

基本上,我想延迟导航栏的加载,直到 $rootScope.signedIn 被加载并且是真或假,并且 $rootScope.currentUser 是一个对象或未定义。

我尝试在 app.config 路由中创建 Promise,但我不确定如何将 Promise 返回到永久视图状态。

感谢任何帮助。

编辑

这是我在其中广播我的登录信息的服务。这会在用户通过身份验证/登录或注销时触发:

    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');
        }
    });

该服务通过以下方式注入到 NavCtrl 控制器中:

    $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;
    });

此控制器的模板是 nav.html,如下所示:

<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>

同样,此视图绑定到 NavCtrl。登录用户时,我使用 AuthCtrl 如下:

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

当我尝试登录时,导航视图不会使用新值更新,尽管广播是从“登录”的服务中触发的。

认证服务:

'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;


});

【问题讨论】:

  • 也许ngCloak 可以帮助你。或者 $timeout 可能...
  • 我尝试将 ng-cloak 放在 nav.html 视图的顶部 div 上,但没有解决问题。
  • 你也应该试试服务而不是$rootScope
  • 我开始使用服务来获取我当前的用户详细信息,但我实际上在每个页面和视图上都调用了我的用户对象信息 - 这不是 $rootScope 的用例吗?另外,这并不能解决我的问题。
  • 除了广播之外,$rootScope 没有用例,您也应该避免使用这些。服务是持久的(有点像单例),所以$scope.foo = someService.getSomeData(); 没有任何危害。

标签: javascript angularjs angular-promise


【解决方案1】:

在菜单上添加一点 $rootScope.$broadcast 和 ng-hide 就可以轻松完成。看到这个plunker

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="angular.js@1.2.x" src="https://code.angularjs.org/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>

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;
  });
});

【讨论】:

  • 我已成功将登录/注销事件广播到 NavCtrl 控制器,但是,在用户成功登录后,导航栏不会改变,直到用户单击某些内容。
  • “导航栏在用户点击某些东西之前不会改变”是什么意思?据我了解您的问题,您想根据用户的登录状态显示或隐藏导航栏。
  • 广播似乎注册了,因为 NavCtrl console.logs “登录”,但是当用户登录时视图由于某种原因没有更新......用户必须在导航栏视图更改
  • 你试过我的 plunker 样品了吗?这是你想要的行为吗?
  • 这是我想要的行为。但是,广播只是在页面加载时更改视图/设置状态,而不是在用户登录时作为瞬时更改。
【解决方案2】:

好的,如果我建议的方式不适合你,这里是第二种可能的解决方案 (plunker)

基本思想是有一个服务(在这种情况下是一个工厂),您可以在其中设置登录用户名,然后在导航控制器中使用 $watch 来观察服务中身份验证状态的变化。和代码:

<!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="angular.js@1.2.x" src="https://code.angularjs.org/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>

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);


});

和服务:

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

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

【讨论】:

  • 嗯,我尝试将 isLoggedIn 函数与控制器 $watch 一起添加,但我的身份验证服务比立即将用户设置为当前用户更复杂。它必须进行身份验证,然后通过他们的 ID 号找到用户。我将在上面发布整个工厂。
  • 好吧,我怀疑您的身份验证代码中存在一些复杂性,导致代码无法正常工作。如果您有另一个依赖步骤(如通过 id 查找用户),则需要使用 Promise。无论如何,我很高兴你解决了你的问题。
  • 感谢您的所有帮助。
【解决方案3】:

@zszep $broadcast answer 解决了一个问题。有必要在 NavCtrl 中的每个 $scope.isHidden 命令之后添加$scope.$apply()。这会强制刷新页面并更新导航视图。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-25
    • 2016-04-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多