简体   繁体   English

AngularJS - 如果用户已登录,则显示/隐藏导航项

[英]AngularJS - show/hide nav items if user is logged in

I have a single-page AngularJS app, working with Express, node.js, and MongoDB via Mongoose. 我有一个单页的AngularJS应用程序,通过Mongoose使用Express,node.js和MongoDB。 Using Passport for user management/authentication. 使用Passport进行用户管理/身份验证。

I'd like the navbar items to change based on whether a user is logged in or not. 我希望根据用户是否登录来更改导航栏项。 I'm having trouble figuring out how to implement it. 我无法弄清楚如何实现它。

I find out if a user is logged in through an http request: 我发现用户是否通过http请求登录:

server.js server.js

app.get('/checklogin',function(req,res){
  if (req.user)
    res.send(true);
  else
    res.send(false);

On the front end, I have a NavController calling this using Angular's $http service: 在前端,我有一个NavController使用Angular的$http服务调用它:

NavController.js NavController.js

angular.module('NavCtrl',[]).controller('NavController',function($scope,$http) {

    $scope.loggedIn = false;

    $scope.isLoggedIn = function() {

      $http.get('/checklogin')
        .success(function(data) {
          console.log(data);
          if (data === true)
            $scope.loggedIn = true;
          else
            $scope.loggedIn = false;
        })
        .error(function(data) {
          console.log('error: ' + data);
        });
    };
};

In my nav, I am using ng-show and ng-hide to determine which selections should be visible. 在我的导航中,我使用ng-showng-hide来确定哪些选项应该可见。 I am also triggering the isLoggedIn() function when the user clicks on the nav items, checking whether the user is logged in during each click. 当用户单击导航项时,我还会触发isLoggedIn()函数,检查用户是否在每次单击期间登录。

index.html 的index.html

<nav class="navbar navbar-inverse" role="navigation">
  <div class="navbar-header">
    <a class="navbar-brand" href="/">Home</a>
  </div>
  <ul class="nav navbar-nav">
    <li ng-hide="loggedIn" ng-click="isLoggedIn()">
      <a href="/login">Login</a>
    </li>
    <li ng-hide="loggedIn" ng-click="isLoggedIn()">
      <a href="/signup">Sign up</a>
    </li>
    <li ng-show="loggedIn" ng-click="logOut(); isLoggedIn()">
      <a href="#">Log out</a>
    </li>
  </ul>
</nav>

Problem 问题

There are other places in my app where the user can log in/out, outside of the scope of the NavController. 我的应用程序中还有其他位置,用户可以在NavController范围之外登录/注销。 For instance, there's a login button on the login page, which corresponds to the LoginController. 例如,登录页面上有一个登录按钮,对应于LoginController。 I imagine there's a better way to implement this across my entire app. 我想在我的整个应用程序中实现这一点有更好的方法。

How can I 'watch' whether req.user is true on the back end and have my nav items respond accordingly? 我如何“观察” req.user是否为true ,并且我的导航项是否相应地响应?

you can use $rootScope to share info across the entire app: 您可以使用$rootScope在整个应用程序中共享信息:

.controller('NavController',function($scope,$http, $rootScope) {

    $scope.isLoggedIn = function() {

      $http.get('/checklogin')
        .success(function(data) {
          console.log(data);
          $rootScope.loggedIn = data;
        })
        .error(function(data) {
          console.log('error: ' + data);
        });
    };
};

now you can change the value of loggedIn from other places in your app by accessing $rootScope.loggedIn in the same way it is done in the code above. 现在,您可以通过访问$rootScope.loggedIn来改变应用程序中其他位置的loggedIn值,就像在上面的代码中一样。

With that said, you should abstract the relevant code into a service and a directive. 话虽如此,您应该将相关代码抽象为服务和指令。 This would allow you to have one central place to handle, log in, log out, and the state of $rootScope.loggedIn . 这将允许您有一个中心位置来处理,登录,注销和$rootScope.loggedIn的状态。 If you post the rest of the relevant code I could help you out with a more concrete answer 如果您发布其余的相关代码,我可以帮助您找到更具体的答案

You can broadcast that event when user logs in successfully. 您可以在用户成功登录时广播该事件。 And no need to keep polling your server if user is logged in you can keep a variable in memory that tells if you have a valid session or not. 如果用户登录,则无需继续轮询您的服务器,您可以在内存中保留一个变量,告知您是否有有效的会话。 You can use a token-based authentication which is set in the server side: 您可以使用在服务器端设置的基于令牌的身份验证:

services.factory('UserService', ['$resource',                                        
  function($resource){

    // represents guest user - not logged
    var user = {
        firstName : 'guest',
        lastName : 'user',
        preferredCurrency : "USD",
        shoppingCart : {
            totalItems : 0,
            total : 0
        },                                                  
    };

    var resource = function() {
        return $resource('/myapp/rest/user/:id', 
            { id: "@id"}
    )};

    return {
        getResource: function() { 
            return resource;
        },

        getCurrentUser: function() {
            return user;
        },

        setCurrentUser: function(userObj) {
            user = userObj;
        },

        loadUser: function(id) {
            user = resource.get(id);
        }
    }

  }]);

services.factory('AuthService', ['$resource', '$rootScope', '$http', '$location', 'AuthenticationService', 
  function ($resource, $rootScope, $http, $location, AuthenticationService) {
    var authFactory = {
        authData: undefined       
    };

    authFactory.getAuthData = function () {
        return this.authData;
    };

    authFactory.setAuthData = function (authData) {
        this.authData = {
            authId: authData.authId,
            authToken: authData.authToken,
            authPermission: authData.authPermission
        };
        // broadcast the event to all interested listeners
        $rootScope.$broadcast('authChanged');
    };

    authFactory.isAuthenticated = function () {
        return !angular.isUndefined(this.getAuthData());
    };

    authFactory.login = function (user, functionObj) {
        return AuthenticationService.login(user, functionObj);          
    };

    return authFactory;
}]);

services.factory('AuthenticationService', ['$resource',
  function($resource){
    return $resource('/myapp/rest/auth/', 
            {},
            {
              'login': { method: "POST" }
            }               
    );
  }]);          

services.factory('authHttpRequestInterceptor', ['$injector',  
 function ($injector) {
    var authHttpRequestInterceptor = {
        request: function ($request) {
            var authFactory = $injector.get('AuthService');
            if (authFactory.isAuthenticated()) {
                $request.headers['auth-id'] = authFactory.getAuthData().authId;
                $request.headers['auth-token'] = authFactory.getAuthData().authToken;
            }
            return $request;
        }
    };

    return authHttpRequestInterceptor;
}]);

controller: 控制器:

controllers.controller('LoginCtrl', ['$scope', '$rootScope', 'AuthService', 'UserService', 
  function LoginCtrl($scope, $rootScope, AuthService, UserService) {
    $scope.login = function () {
        AuthService.login($scope.userInfo, function (data) {
            AuthService.setAuthData(data);
            // set user info on user service to reflect on all UI components
            UserService.setCurrentUser(data.user);
            $location.path('/home/');               
        });
    };

    $scope.isLoggedIn = function () {
        return AuthService.isAuthenticated();
    }

    $scope.user = UserService.getCurrentUser();         
}])

You can add user's session data inside the index.html using some templating library like EJS. 您可以使用一些模板库(如EJS)在index.html中添加用户的会话数据。

Just add ejs middleware: 只需添加ejs中间件:

 var ejs = require('ejs'); // Register ejs as .html. app.engine('.html', ejs.__express); 

And then, when returning the index.html render the session data into the response. 然后,在返回index.html时将会话数据呈现到响应中。

  res.render( "/index.html", { session : { user_data : JSON.stringify(req.user) } }); 

You'll now have access to this data in the index.html, now you need to load it into Angular app. 您现在可以在index.html中访问此数据,现在需要将其加载到Angular应用程序中。 I used preload-resource example, but you can use your own way. 我使用了preload-resource示例,但您可以使用自己的方式。

You can also use $localStorage if you want the login to persist outside of the current session. 如果您希望登录在当前会话之外保持不变,也可以使用$ localStorage。 I've found this library has been super helpful for these types of situations. 我发现这个库对这些类型的情况非常有帮助。 ( https://github.com/grevory/angular-local-storage ) https://github.com/grevory/angular-local-storage

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM