Angular 1.5 $routerOnActivate on Root Router

3.1k Views Asked by At

I want to make sure that the user is logged in properly before proceeding to any of the components he/she's trying to reach, if they're not logged in. Send them to login.

My idea is to do a check in the $routerOnActivate in the root router. Which to me would solve the issue for any sub routes.

However nothing seems to happen if i just try to log something. Example:

angular
    .module('app')
    .config(function($locationProvider) {
        $locationProvider.html5Mode(true);
    })
    .value('$routerRootComponent', 'app')
    .component('app', {
          templateUrl:'landing.html',
          controller: MainController,
          $routeConfig: [
                { path: '/', name: 'Dashboard', component: 'dashboard', useAsDefault: true },
                { path: '/media', name: 'Media', component: 'media'}
                ...
          ]
    });


function MainController(){
    this.$routerOnActivate = function(next, previous){
        console.log('activated', next, previous);
    };
}

The same code this.$routerOnActivate works if i put it in any of the Components which are specified in the routeConfig. However obviously I don't want to make the same check in every component, but rather solve it once globally.

What is the approach for 1.5?

2

There are 2 best solutions below

0
On BEST ANSWER

Paste from my comment as requested

What about perform your check on the loading on the page ? This would run perfectly in an angular.run.

And if you want to handle session expiration, you can add an interceptor to all requests and watch for a 401 response.

10
On

Instead of performing a check when you load a page, use following:

Angular has a special component lifecycle hook to handle these edge cases named $canActivate - to check whether it should try to activate it or not. You may talk to a authentication/authorization service to validate that for a given user's state, are they allowed to reach your component or not.

This way your component will be smart enough to encapsulate any checks required to activate itself.

Also, you can inject any service like $http or your custom service to talk to your server. In the code snippet below, I mimic this call using $timeout that just returns true, if you return false, your component will not activate.

angular.module('app').component('someComponent', {
    template: 'this is an inline component template',
    $canActivate: function ($timeout) {
        return $timeout(function(){
            return true;
        }, 2000);
    }
}); 

Use another hook named $routerOnActivate to read the next and previous routes. If you are interested in the params of a route, use next.params which is an object that will always have the parameters that were passed to that route. e.g. next.params.id where id is the parameter that was passed to the requested route.

Use $canActivate using TypeScript:

I've written a post regarding how to use write AngularJS components in TypeScript and have some drafts to use router lifecycle hooks that I'll publish today. Till then, here is the code to use some hooks using TypeScript below:

class ReviewDetailsComponent implements ng.IComponentOptions {
        templateUrl = 'app/components/review-details.component.html';

        // function member for this hook doesn't work!!
        // so either use lambda expression or function directly.
        $canActivate = $timeout => $timeout(() => true, 3000);

        controllerAs = 'model';
        controller = ['$http', ReviewDetailsController];
    }

angular.module('app').component('reviewDetails', new ReviewDetailsComponent());

The typescript code above is same as javascript code snippet above that uses $canActivate.

Unfortunately, it didn't worked when this is defined as a function member in class like $canActivate() and the generated javascript is this member defined using prototype like ReviewDetailsComponent.prototype.$canActivate.

But it works well when written using lambda expression syntax or a function directly. If you are using a class to define a component, it is good to choose lambda expression in this case.

Use $routerOnActivate using TypeScript

The linked controller in this case also uses another lifecycle hook named $routerOnActivate and this works well if defined as a function member:

interface IReviewDetailsController {
    id: number;
    $routerOnActivate(next, previous);
}

class ReviewDetailsController implements IReviewDetailsController {

    id: number;

    constructor(private $http: angular.IHttpService) { }

    $routerOnActivate(next, previous) {
        this.id = next.params.id;

        // talk to cache or server to get item by id
        // & show that on UI
    }
}