Check condition before loading route in Angular JS

December 14, 2014
by Rahil Shaikh

In this tutorial we will be using resolve of angular js routes to validate a condition and take action before loading the template into the view.Access control in single page angular applications which are using routes.
To demonstrate this I’ll be building a simple app in this tutorial.
I’ve provided the detailed explanation below but for people already knowing the working of routes in angular js here’s how you can use resolve in routes.

$routeProvider
    .when('/somepage' ,{
    templateUrl: "templates/template.html",
    resolve:{
        "check":function($location){  
            if('Your Condition'){
                //Do something
            }else{
                $location.path('/');    //redirect user to home.
                alert("You don't have access here");
            }
        }
    }
    })

Note:You can also use promise for this,resolve and reject that promise based on the condition.

Checkout the demo: here
Download the code:here

There are cases some times when you want to only allow a user to a page/route only if some condition is satisfied.
A typical use-case would be having a user base with different roles , admins who can access all the pages and normal users who only have access to selected pages.
Or may be to validate session and take action.

Most of the people end up writing the logic controller which ofcourse works but the user could see a flash of that inner page before he is redirected.

Resolve provides  a map of dependencies to be injected into the controller and the router waits for the dependecies to be ressolved before the controller is instantiated, so here we can do some check and add our own logic for pass or fail.

Lets first  have a look at our Code.

index.html
<html ng-app="testApp">
    <head>
        <link href="css/bootstrap.min.css" rel="stylesheet" media="screen" />
        <link href="css/bootstrap-theme.min.css" rel="stylesheet" media="screen"/>
    </head>
    <body ng-view>         
    </body>
    <script src="js/angular.js"></script>
    <script src="js/angular-route.min.js"></script>
    <script src="js/testApp.js"></script>
</html>

Please note you will have to include the angular route file for this to work.Can be downloaded here.
Our partials would be loaded inside the body where we have added the ng-view directive.

Partial files:
home.html
URL- <host>/
This would be our landing page from where a user can either go to commonpage or secretpage.
commonpage.html
URL-<host>/commonpage
This would be accessible to all users.
secretepage.html – This would only be accessible to users with permission.
URL-<host>/secretpage.

Note:I haven’t shown the code for commonpage.html and secretepage.html as they are simple html partials but they can be found in the downloaded code.

home.html
<div class="container">
    <div class="row" ng-controller="testCtrl">
        <h1>Hi I'm the Main Page</h1>
        <div class="col-md-4">
            <h4>Hey Every one can access me!</h4>
            <a href="#/commonpage" class="btn btn-block">Common Page</a>
        </div>
        <div class="col-md-4">
            <h4>Only people with access can see me!</h4>
            <button class="btn btn-danger btn-block" ng-click="getAccess()">Click Me to get Access!</button>
            <a href="#/secretpage" class="btn btn-block">Secret Page</a>
        </div>
        <div class="col-md-4"></div>
    </div>
</div>

Above we have two links one leads to commonpage and other to secretpage , we have defined a controller as testCtrl , We also have a button which calls getAccess() method in the testCtrl and provides the user access to secret page.

testApp.js
var testApp = angular.module('testApp',['ngRoute']);

testApp.config(function($routeProvider){
    $routeProvider
    .when('/secretpage' ,{
    templateUrl: "templates/secretpage.html",
    resolve:{
        "check":function(accessFac,$location){   //function to be resolved, accessFac and $location Injected
            if(accessFac.checkPermission()){    //check if the user has permission -- This happens before the page loads
               
            }else{
                $location.path('/');                //redirect user to home if it does not have permission.
                alert("You don't have access here");
            }
        }
    }
    })
   
    .when('/commonpage' ,{
    templateUrl: "templates/commonpage.html"
    })

    .when('/' ,{
    templateUrl: "templates/home.html"
    });
});


testApp.factory('accessFac',function(){
    var obj = {}
    this.access = false;
    obj.getPermission = function(){    //set the permission to true
        this.access = true;
    }
    obj.checkPermission = function(){
        return this.access;             //returns the users permission level
    }
    return obj;
});

testApp.controller('testCtrl',function($scope,accessFac){
    $scope.getAccess = function(){
        accessFac.getPermission();       //call the method in acccessFac to allow the user permission.
    }
});

Explaination for testApp.js
Remember inject the ngRoute dependency to your app,or you application wont work.

var testApp = angular.module('testApp',['ngRoute']);

The accessFac factory:
It has two methods getPermission()gives the user the permission,checkPermission() returns the current user’s permission

The testCtrl:
Here I’ve defined a function which is called on click of the get permission button , which in turn calls the getPermission() function of accessFac and thus sets the access to true.
Remember to inject accessFac into the controller.

testApp.controller('testCtrl',function($scope,accessFac){
....
}

The routes:

testApp.js routes explaination
testApp.config(function($routeProvider){
    $routeProvider
    .when('/secretpage' ,{    
    templateUrl: "templates/secretpage.html",
    resolve:{
        "check":function(accessFac,$location){   //function to be resolved, accessFac and $location Injected
            if(accessFac.checkPermission()){    //check if the user has permission -- This happens before the page loads
               
            }else{
                $location.path('/');                //redirect user to home if it does not have permission.
                alert("You don't have access here");
            }
        }
    }
    })
    ....
.........
......
});

Angular Routes provides us with $routeProvider which helps us to define route paths and the template to be loaded inside ng-view when that url is called.

Resolve Explanation :
Here I’ve first injected the dependency of acessFac and $location (look at the above code snippet for reference), then I’m chceking if the user has permission by invoking the checkPermission() function in accessFac,if the user does not have the permission I redirect him to the home page.
Note:This can also be done in a controller for that secret page , but this will first render the template initialize the controller and then redirect to the home page.

I hope this tutorial helps you in your future angular applications.

About

Engineer. Blogger. Thinker. Loves programming and working with emerging tech. We can also talk on Football, Gaming, World Politics, Monetary Systems.

Free PDF

Subscribe and get AngularJS Tips PDF. We never spam!
First Name:
Email:

25 comments

  1. Moiz
    |

    Awesome post man , This was a great help 🙂

  2. Akeel
    |

    Nice and clean.. Thank you!!!!

  3. |

    Thanks very very much! You rocks! 🙂

  4. |

    Hi, this post is interesting but there still is a problem : when we click on ‘secret page’ link without having the permission, the page doesn’t display but the request is made and we can have access to the page’s content through the dev tools (Request’s response).
    So i wonder if there is a solution to that problem, can we prevent user to have access to this data ?

    • |

      You can do it using promises, basically reject the promise when the condition fails, I’ll confirm once I verify and also provide the code.

  5. Cory
    |

    Thanks Rahil!

    This was a huge help. I had to do some extra work to get it to work with a promise but your solution pointed me in the right direction.

  6. Shrinivas
    |

    Hello, thanks for the nice post. I am facing some problems. It will be great if you could help me. I have my cofig as:

    .when(‘/ONE’, {
    templateUrl: ‘abc/def/ghi/one.html’,
    controller: ‘oneCtrl’,
    controllerAs: ‘someOne’,
    resolve:{
    //load this template only if user is logged in
    “check”:function($location, $cookies){
    if($cookies.get(“isLogged”)===”true”){
    }else{
    $location.path(‘/’); //redirect user to login Page.
    alert(“Please Login to access this page”);
    }
    }
    }
    })

    .when(‘/TWO’, {
    templateUrl: ‘abc/def/ghi/two.html’,
    controller: ‘twoCtrl’,
    controllerAs: ‘someTwo’,
    resolve:{
    “check”:function($location, $cookies){
    if($cookies.get(“isLogged”)===”true”){
    }else{
    $location.path(‘/’); //redirect user to login Page.
    alert(“Please Login to access this page”);
    }
    }
    }
    })

    The template is getting loaded only when the user is logged in. (i.e. resolve is working as expected). However even if the user is not logged in, Controller for that template is getting executed. I have some console.log() in oneCtrl and twoCtrl, which can be seen in the console, even if the user is not logged.

    • |

      A better approach then above would be to use promise ($q defer).

    • Shrinivas
      |

      Adding more clarity to my question:

      My default page is login page. After successful login, page one.html will be displayed. Being in login page, if the user enters the URL of other pages (say /one), one.html will not be rendered. still login page will be shown, with the alert. But when I check the console, consoles present in oneCtrl will be displayed.

  7. Bob Turin
    |

    Hi Rahil.. what about loading a different CONTROLLER based on some condition? Is that possible?

  8. Marcos
    |

    Could I just do something like this in the resolve function?

    if (!accessFac.checkPermission()) {
    $location.path(‘/’);
    alert(“You don’t have access here”);
    }

    Otherwise, what would I put inside this:

    if (accessFac.checkPermission()) {
    // What goes here?
    } else {

    }

    • |

      Yes you can do that. It depends on your needs. You can do any pre-processing required in the if block. For eg. You can make an api call get the data and then pass it to the controller. This way your UI will load along with the data.

  9. dilip
    |

    thank you very much

  10. Dave
    |

    I love the idea and the simplicity however(and I may be wrong here) but isn’t it hugely insecure to have access control on the client side? Since the logic is performed in the browser I would think it can be overruled in Dev tools simply by modifying the testApp.js permissions methods to always return valid, as a single page app it wouldn’t refresh to override the modified code and would just allow access, the security would be bypassed, or am i missing something? Please let me know it is very interesting

    • |

      Your concern is completely valid, however that is the nature of applications that run on Ajax/ Rest APIs. The security of the application completely depends on how you handle it, if you are only handling it on the front-end than you application is at risk. But usually how you would take care of these scenarios is that you will add proper checks and validations in the back-end and prevent the user from entering the page using the front-end (Coz these pages are directly served on the client side). So even if like you said, if someone alters some code and manages to enter the page the sensitive data on the page (which will be fetched from the API) will not load since the back-end validates the request(So all the attacker will have is the html markup) similarly the post request(if any) would also not work.

      The other option you have is to serve the complete page from the server. But this will slow down your application and also would not work properly with the nature of Single page angular.js applications.

      This post gives an idea on how you can power you applications using resolve, there is more you can do with it for example pre load data and inject into the controller before it instantiates.

      Hope this helps.

  11. Sagar
    |

    Following url redirect is not working. Alert in if blocks getting called. But path redirect.

    if( token == ‘loggedOut’ )
    {
    $location.url(‘/’);
    }
    if( token == ‘loggedIn’ )
    {
    $location.path(‘app/index.html’);
    }

    • |

      That’s a wrong way to use the $location.path. It takes in the route url and not the html file.

  12. jana
    |

    Good Work !!

  13. test
    |

    Hi, I desire to subscribe for this web site to take most up-to-date updates, therefore where can i do it please assist.

    • |

      You can subscribe via the form available on the right sidebar or within the footer section.

Leave a Comment