complete login form example with AngularJS -


during last week have created complete example web-application demonstrate custom login form implementation angularjs. not easy me because java developer , not have experience in javascript. code works fine. hurray :-)

i share solution , happy if have @ , suggest me simplification. please feel free criticize javascript code.

in demo application server-side resources (images, html pages , restful api) protected openam.

in order avoid mistakes , complicated configuration of protected urls in openam created 2 folders in web application, 1 public content , 1 protected content.

the structure of war file (web-app) looks this:

web-ui.war |-app |  |-components |     |-protected <-- protected web folder 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 

the openam configuration quite easy because of proper folder structure. values of 'not enforced uri (public urls without protection)':

  • /web-ui/
  • /web-ui-assets/*
  • /web-ui/app/components/public/*
  • /loginservlet/auth

the first 3 urls located inside demo web application (war file). have public url. authentication java servlet recieves username/password , pass them openam. openam produce proper token , servlet sends client's browser.

i use angularjs v1.4.7, ui-bootstrap v0.14.2 , angular-ui v0.2.15 routing.

my index.html page simple:

<!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 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 file:

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

i have created 2 directives handle onenter event inside text box , set focus on (show blinking cursor) text element.

app-directive.js file:

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

the important part of app-routes.js file 'data : { requireslogin: true }'. line tells page routing logic on client side user wants access protected content. if user not logged-in log-in page needs shown , after successful log-in process original requested page needs displayed.

another important line in js file related '$statechangestart' event. fires every time when url changed (template html routing starts work). in case requested url stored loginfactory in case of login page needs displayed. after login process original requested page needs displayed need information further use.

app-routes.js file:

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

in login.html file autofocus directive used on username text box , my-enter directive used on password button. if user hit enter after password typed in login() method controller called. same behaviour applied when user clicks on login button.

before login.html displayed call reset() method of controller. cleans value of previous password text box , hide 'invalid username or password' label.

login.html file without design elements:

<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 sends username , password auth servlet , redirect user next, requested page. login.factory.js file:

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

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

that's it.

i have questions:

  1. lazy loading of js files /app/components/protected area: keep template related js files (ex.: controllers) in same directory html files , load them on fly template html files. there existing best practice this?

  2. my auth servlet sends cookie token browser , browser sents token cookie server when new url requested. if token inside cookie correct openam give access protected resources. read cookie not safety way store token info. instead of use html5 feature: store in browser's locale storage , send automatically server every requests. implement this?

  3. this question related directives: if rename 'myenter' directive 'onenter' or 'customenter' o 'caenter' wont work anymore. why? can use 'my' prefx befote name of directives?

  4. i tried follow suggested naming convention angularjs related javascript files , have many of them. uncomfortable include of them index.html file , think not performance of web application. there best practice merge/combine 'millions' separated javascript files 1 before application goes production server?


Comments

Popular posts from this blog

javascript - Slick Slider width recalculation -

jsf - PrimeFaces Datatable - What is f:facet actually doing? -

angular2 services - Angular 2 RC 4 Http post not firing -