title: complete login form example with AngularJS
body: <p>During the last week I have created a complete example web-application to demonstrate a custom login form implementation with AngularJS. It was not too easy for me because I am a Java developer and I do not have too much experience in JavaScript. But finally my code works fine. Hurray :-)</p>
  
  <p>I would like to share my solution with you and I would be happy if you could have a look at it and suggest me any simplification. Please feel free to criticize my JavaScript code.</p>
  
  <p>In my demo application the server-side resources (images, html pages and RESTFul api) are protected by OpenAM.</p>
  
  <p>In order to avoid mistakes and complicated configuration of protected urls in OpenAm I created two folders in my web application, one for public content and another one for protected content.</p>
  
  <p>The structure of my war file (web-app) looks like this:</p>
  
  <pre><code>web-ui.war
  |-app
  |  |-components
  |     |-protected &lt;-- 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
  </code></pre>
  
  <p>The OpenAM configuration is quite easy because of my proper folder structure.
  Values of 'Not Enforced URI (public urls without any protection)':</p>
  
  <ul>
  <li>/web-ui/</li>
  <li>/web-ui-assets/*</li>
  <li>/web-ui/app/components/public/*</li>
  <li>/loginservlet/auth</li>
  </ul>
  
  <p>The first three urls are located inside my demo web application (war file). I have another public url. It is an authentication java servlet which recieves username/password and pass them to OpenAM. OpenAM will produce a proper token and my servlet sends it back to the client's browser. </p>
  
  <p>I use AngularJS v1.4.7, ui-bootstrap v0.14.2 and angular-ui v0.2.15 for routing.</p>
  
  <p>My index.html page is very simple:</p>
  
  <pre><code>&lt;!DOCTYPE html&gt;
  &lt;html lang="en" ng-app="myApp"&gt;
      &lt;head&gt;
          &lt;meta charset="utf-8"&gt;
          &lt;title&gt;MyApp&lt;/title&gt;
  
          &lt;!-- Bootstrap Core CSS --&gt;
          &lt;!--&lt;link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" type="text/css"&gt;--&gt;
          &lt;link rel="stylesheet" href="assets/libs/bootstrap/3.3.5/css/bootstrap.min.css" type="text/css"&gt;
  
          &lt;!-- Custom CSS --&gt;
          &lt;link rel="stylesheet" href="assets/css/app.css" type="text/css"&gt;
      &lt;/head&gt;
      &lt;body&gt;
          &lt;!-- Page Header --&gt;
          &lt;header id="navigation-bar"&gt;
                  &lt;div class="..." ng-click="navCollapsed = true"&gt;
                      &lt;ul class="nav navbar-nav navbar-right"&gt;
                          &lt;li&gt;&lt;a ui-sref="protected/welcome"&gt;private page&lt;/a&gt;&lt;/li&gt;
                          &lt;li&gt;&lt;a ui-sref="help-general"&gt;Help&lt;/a&gt;&lt;/li&gt;
                          &lt;li&gt;&lt;a ui-sref="sign-up"&gt;Register&lt;/a&gt;&lt;/li&gt;
                          &lt;li&gt;&lt;a ui-sref="log-in"&gt;Login&lt;/a&gt;&lt;/li&gt;
                      &lt;/ul&gt;
                  &lt;/div&gt;
              &lt;/nav&gt;
          &lt;/header&gt;
  
          &lt;!-- Page Body --&gt;
          &lt;div ui-view&gt;&lt;/div&gt;
  
          &lt;!-- AngularJS --&gt;
          &lt;script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular.min.js"&gt;&lt;/script&gt;
          &lt;script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.15/angular-ui-router.js"&gt;&lt;/script&gt;
  
          &lt;!-- Angular UI Bootstrap --&gt;
          &lt;script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/0.14.2/ui-bootstrap-tpls.min.js"&gt;&lt;/script&gt;
  
          &lt;!--  Application JavaScript files --&gt;
          &lt;script src="app/components/public/app-module.js"&gt;&lt;/script&gt;
          &lt;script src="app/components/public/app-directive.js"&gt;&lt;/script&gt;
          &lt;script src="app/components/public/app-routes.js"&gt;&lt;/script&gt;
  
          &lt;!-- JavaScripts for Login page --&gt;
          &lt;script src="app/components/public/log-in/logIn-controller.js"&gt;&lt;/script&gt;
          &lt;script src="app/components/public/log-in/logIn-factory.js"&gt;&lt;/script&gt;
      &lt;/body&gt;
  &lt;/html&gt;
  </code></pre>
  
  <p>app-module.js file:</p>
  
  <pre><code>var app = angular.module("myApp", ['ui.bootstrap', 'ui.router']);
  </code></pre>
  
  <p>I have created two directives to handle onEnter event inside a text box and set focus on (show blinking cursor) a text element.</p>
  
  <p>app-directive.js file:</p>
  
  <pre><code>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);
          }
      };
  });
  </code></pre>
  
  <p>The important part of the app-routes.js file is '<em>data : { requiresLogin: true }</em>'. This line tells to the page routing logic on the client side that user wants to access to a protected content. If the user is not logged-in then log-in page needs to be shown and after the successful log-in process the original requested page needs to be displayed.</p>
  
  <p>Another important line in this js file is related to the '$stateChangeStart' event. It fires every time when the url is changed (template html routing starts to work). In that case the requested url is stored into the loginFactory in case of login page needs to be displayed. After the login process the original requested page needs to be displayed so we need this information for further use. </p>
  
  <p>app-routes.js file:</p>
  
  <pre><code>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 &amp;&amp; toState.data.requiresLogin &amp;&amp; !loginFactory.isLoggedIn();
  
          if (isAuthenticationRequired == true) {
              loginFactory.setToState(toState);
              event.preventDefault();
              $state.go('log-in');
          }
      });
   });
  </code></pre>
  
  <p>In my login.html file the autoFocus directive is used on username text box and my-enter directive is used on password button. If the user hit enter after the password is typed in then the login() method from the controller is called. Same behaviour is applied when the user clicks on the login button.</p>
  
  <p>Before the login.html is displayed we call the reset() method of the controller. It cleans the value of the previous password text box and hide the 'Invalid username or password' label.</p>
  
  <p>login.html file without any design elements:</p>
  
  <pre><code>&lt;div class="..." ng-controller="loginController" data-ng-init="reset()"&gt;
      &lt;h3&gt;Login or &lt;a ui-sref="user-registration"&gt;Sign up&lt;/a&gt;&lt;/h3&gt;
  
  &lt;div class="..."&gt;
      &lt;div class="col-xs-12 col-sm-6"&gt;
          &lt;input type="text" class="..." placeholder="email address" ng-model="user.username" auto-focus&gt;
          &lt;input type="password" class="..." placeholder="Password" ng-model="user.password" my-enter="login()"&gt;
  
          &lt;span class="..." ng-if="!firstAttempt"&gt;Invalid username or password&lt;/span&gt;
          &lt;span class="..." ng-if="firstAttempt"&gt;&lt;/span&gt;
  
          &lt;button class="..." ng-click="login()"&gt;Login&lt;/button&gt;
      &lt;/div&gt;
  &lt;/div&gt;
  </code></pre>
  
  <p>login-factory.js sends username and password to the auth servlet and redirect user to the next, requested page.
  login.factory.js file:</p>
  
  <pre><code>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 + '&amp;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;
              });
          }
      };
  });
  </code></pre>
  
  <p>login-controller.js file:</p>
  
  <pre><code>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 = '';
      }
  });
  </code></pre>
  
  <p>That's it.</p>
  
  <p>I have some questions:</p>
  
  <ol>
  <li><p>Lazy loading of js files from the /app/components/protected area: I
  would like to keep template related js files (ex.: controllers) in
  the same directory as html files and load them together on the fly with
  the template html files. Is there any existing best practice for this?</p></li>
  <li><p>My auth servlet sends back a cookie with token to the browser and the
  browser sents token cookie back to the server when a new url is
  requested. If the token inside the cookie is correct then OpenAM give
  access to the protected resources. I read that cookie is not a safety
  way to store token info. Instead of I would like to use a HTML5
  feature: store it in the browser's locale storage and send it
  automatically to the server with every requests. Could you help my to implement this?</p></li>
  <li><p>This question is related to directives: If I rename the 'myEnter' directive to 'onEnter' or 'customEnter' o 'caEnter' then it wont work anymore.
  Why? Can I only use 'my' prefx befote the name of the directives? </p></li>
  <li><p>I tried to follow the suggested naming convention for AngularJS
  related JavaScript files and now I have many of them. It is so
  uncomfortable to include all of them into the index.html file and I
  think it is not good to the performance of the web application. Is
  there any best practice to merge/combine the 'millions' separated
  JavaScript files into one before the application goes to the production server?</p></li>
  </ol>