AngularJS

时间:2015-11-01 19:36:30

标签: angularjs login angular-ui

在上周,我创建了一个完整的示例Web应用程序,以演示使用AngularJS的自定义登录表单实现。这对我来说并不容易,因为我是一名Java开发人员,而且我没有太多的JavaScript经验。但最后我的代码工作正常。华友世纪: - )

我想与您分享我的解决方案,如果您能看一下并建议我进行任何简化,我会很高兴。请随意批评我的JavaScript代码。

在我的演示应用程序中,服务器端资源(图像,html页面和RESTFul api)受OpenAM保护。

为了避免OpenAm中受保护网址的错误和复杂配置,我在我的网络应用程序中创建了两个文件夹,一个用于公共内容,另一个用于受保护内容。

我的war文件(web-app)的结构如下所示:

web-ui.war
|-app
|  |-components
|     |-protected <-- protected web folder by OpenAM
|     |  |-welcome
|     |     |-welcome.html
|     |-public
|     |  |-help
|     |  |  |-help-general.html
|     |  |  |-help-howItWorks.html
|     |  |-home
|     |  |  |-home.html
|     |  |-log-in
|     |  |  |-logIn.html
|     |  |  |-logIn-controller.js
|     |  |  |-logIn-factory.js
|     |  |-sign-up
|     |  |  sign-up.html
|     |  |-app-directive.js
|     |  |-app-module.js
|     |  |-app-route.js
|- assets
|   |-css
|   |  |-app.css
|   |-img
|   |-libs
|   |  |-angular
|   |  |  |-1.4.7
|   |  |     |...
|   |  |-angular-ui
|   |  |  |-0.2.15
|   |  |     |...
|   |  |-bootstrap
|   |  |  |-3.3.5
|   |  |     |...
|   |  |-ui-bootstrap
|   |  |  |-0.14.2
|   |  |     |...
|-WEB-INF
|-index.html

由于我的文件夹结构合适,OpenAM配置非常简单。 “未强制URI(没有任何保护的公共网址)”的值:

  • / Web的UI /
  • / Web的UI的资产/ *
  • / Web的用户界面/应用/组件/公共/ *
  • / loginservlet / AUTH

前三个网址位于我的演示网络应用程序(war文件)中。我有另一个公共网址。它是一个身份验证java servlet,它接收用户名/密码并将它们传递给OpenAM。 OpenAM将生成一个正确的令牌,我的servlet将它发送回客户端的浏览器。

我使用AngularJS v1.4.7,ui-bootstrap v0.14.2和angular-ui v0.2.15进行路由。

我的index.html页面非常简单:

<!DOCTYPE html>
<html lang="en" ng-app="myApp">
    <head>
        <meta charset="utf-8">
        <title>MyApp</title>

        <!-- Bootstrap Core CSS -->
        <!--<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" type="text/css">-->
        <link rel="stylesheet" href="assets/libs/bootstrap/3.3.5/css/bootstrap.min.css" type="text/css">

        <!-- Custom CSS -->
        <link rel="stylesheet" href="assets/css/app.css" type="text/css">
    </head>
    <body>
        <!-- Page Header -->
        <header id="navigation-bar">
                <div class="..." ng-click="navCollapsed = true">
                    <ul class="nav navbar-nav navbar-right">
                        <li><a ui-sref="protected/welcome">private page</a></li>
                        <li><a ui-sref="help-general">Help</a></li>
                        <li><a ui-sref="sign-up">Register</a></li>
                        <li><a ui-sref="log-in">Login</a></li>
                    </ul>
                </div>
            </nav>
        </header>

        <!-- Page Body -->
        <div ui-view></div>

        <!-- AngularJS -->
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular.min.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.15/angular-ui-router.js"></script>

        <!-- Angular UI Bootstrap -->
        <script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/0.14.2/ui-bootstrap-tpls.min.js"></script>

        <!--  Application JavaScript files -->
        <script src="app/components/public/app-module.js"></script>
        <script src="app/components/public/app-directive.js"></script>
        <script src="app/components/public/app-routes.js"></script>

        <!-- JavaScripts for Login page -->
        <script src="app/components/public/log-in/logIn-controller.js"></script>
        <script src="app/components/public/log-in/logIn-factory.js"></script>
    </body>
</html>

app-module.js文件:

var app = angular.module("myApp", ['ui.bootstrap', 'ui.router']);

我创建了两个指令来处理文本框中的onEnter事件,并将焦点设置为(显示闪烁的光标)文本元素。

app-directive.js文件:

app.directive('myEnter', function () {
    return function (scope, element, attrs) {
        element.bind("keydown keypress", function (event) {
            if(event.which === 13) {
                scope.$apply(function (){
                    scope.$eval(attrs.myEnter);
                });

                event.preventDefault();
            }
        });
    };
});

app.directive('autoFocus', function($timeout) {
    return {
        restrict: 'AC',
        link: function(_scope, _element) {
            $timeout(function(){
                _element[0].focus();
            }, 0);
        }
    };
});

app-routes.js文件的重要部分是' data:{requiresLogin:true} '。该行告诉客户端用户想要访问受保护内容的页面路由逻辑。如果用户未登录,则需要显示登录页面,登录成功后,需要显示原始请求页面。

此js文件中的另一个重要行与'$ stateChangeStart'事件有关。每次更改URL时都会触发(模板html路由开始工作)。在这种情况下,如果需要显示登录页面,则将请求的URL存储在loginFactory中。在登录过程之后,需要显示原始请求页面,因此我们需要此信息以供进一步使用。

app-routes.js文件:

app.config(function($stateProvider, $urlRouterProvider) {
    $urlRouterProvider.otherwise('/home');

    $stateProvider
        // public pages
        .state('home', {
            url: '/home',
            templateUrl: 'app/components/public/home/home.html'
        })
        .state('log-in', {
            url: '/log-in',
            templateUrl: 'app/components/public/log-in/logIn.html'
        })
        .state('sign-up', {
            url: '/sign-up',
            templateUrl: 'app/components/public/sign-up/signUp.html'//,
        })
        .state('help-general', {
            url: '/help-general',
            templateUrl: 'app/components/public/help/help-general.html'
        })
        .state('help-howItWorks', {
            url: '/help-howItWorks',
            templateUrl: 'app/components/public/help/help-howItWorks.html'
        })

        // private pages
        .state('private/welcome', {
            url: '/private/welcome',
            templateUrl: 'app/components/protected/welcome/welcome.html',
            data : { requiresLogin: true }
        });
});

app.run(function($rootScope, $state, loginFactory) {
    $rootScope.$on('$stateChangeStart', function(event, toState) {
        var isAuthenticationRequired = toState.data && toState.data.requiresLogin && !loginFactory.isLoggedIn();

        if (isAuthenticationRequired == true) {
            loginFactory.setToState(toState);
            event.preventDefault();
            $state.go('log-in');
        }
    });
 });

在我的login.html文件中,autoFocus指令用于用户名文本框,my-enter指令用于密码按钮。如果用户在键入密码后按Enter键,则调用控制器的login()方法。当用户单击登录按钮时,将应用相同的行为。

在显示login.html之前,我们调用控制器的reset()方法。它会清除以前密码文本框的值,并隐藏“无效的用户名或密码”标签。

login.html文件,没有任何设计元素:

<div class="..." ng-controller="loginController" data-ng-init="reset()">
    <h3>Login or <a ui-sref="user-registration">Sign up</a></h3>

<div class="...">
    <div class="col-xs-12 col-sm-6">
        <input type="text" class="..." placeholder="email address" ng-model="user.username" auto-focus>
        <input type="password" class="..." placeholder="Password" ng-model="user.password" my-enter="login()">

        <span class="..." ng-if="!firstAttempt">Invalid username or password</span>
        <span class="..." ng-if="firstAttempt"></span>

        <button class="..." ng-click="login()">Login</button>
    </div>
</div>

login-factory.js将用户名和密码发送到auth servlet,并将用户重定向到下一个请求的页面。 login.factory.js文件:

app.factory('loginFactory', function($http) {
    var loggedIn = false;
    var toState;

    return {
        isLoggedIn: function() {
            return loggedIn;
        },

        setToState: function(state) {
            toState = state;
        },

        callAuthService: function($location, $scope) {
            var formData = 'username=' + $scope.user.username + '&password=' + $scope.user.password;
            var response = $http({
                method: 'POST',
                url: 'http://...:8080/loginservlet/public/auth',
                headers: {'Content-Type': 'application/x-www-form-urlencoded'},
                data: formData})

            .success(function (data, status, headers, config) {
                loggedIn = true;

                if (toState) {
                    $location.path(toState.url);
                    toState = undefined;
                } else
                    $location.path('private/welcome');
            })

            .error(function (data, status, headers, config) {
                loggedIn = false;
            });
        }
    };
});

login-controller.js文件:

app.controller('loginController', function($scope, loginFactory, $location) {
    $scope.loggedIn = false;
    $scope.user = {};
    $scope.firstAttempt = true;

    $scope.login = function() {
        loginFactory.callAuthService($location, $scope);
        $scope.firstAttempt = false;
    };

    $scope.reset = function() {
        $scope.firstAttempt = true;
        $scope.user.password = '';
    }
});

就是这样。

我有一些问题:

  1. 从/ app / components / protected区域延迟加载js文件:I 想保留模板相关的js文件(例如:控制器) 与html文件相同的目录,并与它们一起加载它们 模板html文件。有没有现成的最佳做法?

  2. 我的auth servlet将带有令牌的cookie发送回浏览器和 当新网址出现时,浏览器会将令牌cookie返回给服务器 请求。如果cookie中的令牌是正确的,那么OpenAM给出 访问受保护的资源。我读到cookie不是安全的 存储令牌信息的方法。而不是我想使用HTML5 功能:将其存储在浏览器的语言环境存储中并发送 每次请求都自动到服务器。你能帮我实现吗?

  3. 这个问题与指令有关:如果我将'myEnter'指令重命名为'onEnter'或'customEnter'o'caEnter',那么它将不再起作用。 为什么?我是否只能使用'my'prefx befote指令的名称?

  4. 我尝试遵循建议的AngularJS命名约定 相关的JavaScript文件,现在我有很多。就是这样 将所有这些内容都包含在index.html文件中我感到很不舒服 认为它对Web应用程序的性能不利。是 有任何最佳实践来合并/组合“数百万”分开 在应用程序进入生产服务器之前将JavaScript文件合并为一个文件?

0 个答案:

没有答案