AngularJS Tutorial - 9

New version here

Note that the all tests are present inside the sample, but we will see them at the end of this chapter, when all hopefully will be clear.

The Login service

The entry point will be /api/login

The allowed verbs are

The format of the User will be, in JSON:

    {
        "userid":"userId",
        "password":"userId"
    }

Setting the password with the same value of the userid id will let you login.

The service will add

The cookie is offered as a quick facility to check for the login status. But server side, inside the restricted APIs you need ALWAYS to check the session, since the cookie can be easily manipulated!!!

Login service

This will act as a wrapper on the login API, as the Contacts service in the previous lessons. The only difference is that it will reset the cookie upon logout.

app.factory('LoginService',['$http',
    function($http) {
        return {
            apiBase : 'api/login/',
            http :$http,
            
            isLoggedIn : function() {
                return this.http.get(this.apiBase);
            },
            
            login: function(user){
                return this.http.post(this.apiBase, user);
            },
            
            logout: function(){
                document.cookie = "";
                return this.http.post(this.apiBase+'?request_method=DELETE');
            }
        }
    }
]);

Login controller

It will have the functions

app.controller('LoginController',['$scope', '$location', 'LoginService',
    function($scope,$location, loginService){
        $scope.user = {
            email: "",
            password: ""
        };
        var getCookie= function(name) {
            var cookiename = name + "=";
            var ca = document.cookie.split(';');
            for(var i=0;i < ca.length;i++){
                var c = ca[i];
                while (c.charAt(0)==' ') c = c.substring(1,c.length);
                if (c.indexOf(cookiename) == 0) return c.substring(cookiename.length,c.length);
            }
            return null;
        }
        
        $scope.isLoggedIn = function()
        {
            return getCookie("userid")!=null;
        }
        
        $scope.login = function(user){
            loginService.login(user)
                .success(function(data) {
                    if(data.result=="ko"){
                        $location.path("/login");
                    }else{
                        $location.path("/");
                    }
                });
        }
        $scope.logout = function(){
            loginService.logout()
                .success(function(data) {
                    $location.path("/login");
                });
        }
    }
]);

The "Run"

When starting the application we want that an already logged-in user can be redirected directly to the root path "/".

To do this we add a new function to run during the AngularJs startup, that check the cookie status.

This is inside the assets/login/run.js. A separate file, since we don't want to include this inside the application startup when testing!!

app.run(['$http','LoginService',
    function($http,loginService){
        loginService.isLoggedIn().success(function(data) {
            if(data.result=="ko"){
                window.location = "#/login";
            }else{
                window.location = "#/";
            }
        });
    }
]);

The login view

We create a new view that will contain the standard things and that will be associated to the LoginController

Note the usage of the utility function "isLoggedIn()" to show hide the login/logout button!

<h2>Login</h2>
<div ng-controller="LoginController">
    <form novalidate class="form-horizontal" name="loginForm" id="login-form" method="post" action="" >
        <div class="modal-footer" ng-controller="LoginController" ng-show="isLoggedIn()">
            <button class="btn btn-primary" id="lgin-btn"  
                ng-click="logout()">Logout!</button>
        </div>
        <div ng-show="!isLoggedIn()">
            <div class="control-group">
                <label class="control-label" for="userid">UserId:</label>
                <div class="controls">
                    <input type="text" name="userid"
                        ng-model="user.userid" required ng-disabled="isLoggedIn()"/> <show-error for="userid" ></show-error>
                </div>
            </div>
            
            <div class="control-group">
                <label class="control-label" for="password">Password:</label>
                <div class="controls">
                    <input type="password" name="password"
                        ng-model="user.password" required ng-disabled="isLoggedIn()"//> <show-error use-default for="password"></show-error>
                </div>
            </div>
            <div class="modal-footer">
                <button class="btn btn-primary" id="add-new-btn" 
                    ng-disabled="loginForm.$invalid || (isUnchanged(user) && loginForm.$invalid) || isLoggedIn()" 
                    ng-click="login(user)">Login!</button>
            </div>
        </div>
    </form>
</div>

Modification to the main application files

We added a new route to an empty page as root (index.html), and two new roots, contacts and login

function($routeProvider) {
    $routeProvider
        .when('/', {templateUrl: 'assets/app/index.html',controller: 'EmptyController'})
        .when('/contacts', {templateUrl: 'assets/contact/list.html',controller: 'ContactsListController'})
        .when('/login', {templateUrl: 'assets/login/login.html',controller: 'LoginController'})
        .otherwise({redirectTo: '/'});
}

We should now change our controller.js. Now when an operation is completed we will not redirect to the root but to the "root" of the entity type.

We will then replace, in all functions:

$scope.activePath = $location.path('/');

with

$scope.activePath = $location.path('/'+identifier+'s');

The menu bar.

Now we have several routs, thsu we should add a simple menu. We will use the standard bootstrap pills :P

<div class="nav navbar-nav" ng-controller="HeaderController">
    <ul class="nav nav-pills">
        <li ng-class="{ active: isActive('/')}"><a href="#/">Home</a></li>
        <li ng-class="{ active: isActive('/contacts')}"><a href="#/contacts">Contacts</a></li>
        <li ng-class="{ active: isActive('/login')}"><a  href="#/login">Login/Logout</a></li>
    </ul>
</div>

You see we added a new controller. This is needed to check if the current page is active with the isActive function. This will be a globla controller in app.js

It simply check the location path!

app.controller('HeaderController', ['$scope', '$location',
    function($scope, $location) {
        $scope.isActive = function (viewLocation) { 
            return viewLocation === $location.path();
        };
    }
]);

Screenshot

That's it!

Download the sample source


Last modified on: September 20, 2014