2

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?

Erik Svedin
  • 1,286
  • 12
  • 26
  • 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 – Walfrat Apr 11 '16 at 11:25
  • Hmm yeah, I guess you're completely right. Run block would probably be a perfect place to put this. If you want to post an answer ill mark it as correct. – Erik Svedin Apr 11 '16 at 11:33
  • 1
    What? How would $routerOnActivate "work perfectly" in Angular.run? It is a component lifecycle method. – mikkelrd May 02 '16 at 14:19
  • Given that the check is in the run block, there would be no need for $routerOnActivate. Rather the function responsible for the auth check can be invoked asap – Erik Svedin May 02 '16 at 15:39
  • Just added a solution that is better that performing a check for authentication/authorization on page load. – KKS May 15 '16 at 14:40

2 Answers2

2

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
    }
}
KKS
  • 3,600
  • 1
  • 27
  • 54
  • How to use $canActivate hook in typescript? – Microsoft Developer May 16 '16 at 10:25
  • @MicrosoftDeveloper I've updated my answer and I'll soon publish my draft hopefully today that explains in depth re this concept. – KKS May 16 '16 at 10:54
  • Sorry. I am pretty new to Angular and typescript both. The answer is not clear to me. My purpose is to write hook for $canActivate(), inject service and do some operation. Please refer to my question here http://stackoverflow.com/questions/37251314/canactivate-event-of-component-in-angular-1-5-using-typescript – Microsoft Developer May 16 '16 at 11:02
  • @MicrosoftDeveloper that question is very similar to this one. tell me what is not clear to you? My answer tells you everything how that hook event works and how can you inject a service in it to make a server call and how to write it using typescript – KKS May 16 '16 at 11:54
  • @MicrosoftDeveloper in your question, if `$canActivate` is not logging then it is because the function member definition is not working. Use lambda expression method as explained in the answer above. – KKS May 16 '16 at 12:00
  • This line $canActivate = $timeout => $timeout(() => true, 3000); is not clear to me. If I want to write function, how can I write it? Can I call that function from constructor? – Microsoft Developer May 16 '16 at 12:04
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/112057/discussion-between-microsoft-developer-and-siddharth-pandey). – Microsoft Developer May 16 '16 at 12:29
  • @SiddharthPandey well similar to the other router lifecycle methods this works great if i stick it in every component. I wanted a solution where I could write once for the whole app. I can not get this to work when i put it in my rootComponent, however it works for all the child components. – Erik Svedin May 16 '16 at 12:44
  • @ErikSvedin writing this in a root component should also work. I'll give it a try once I'm free. But ideally, this must be a part of an http interceptor that checks on every http call, if 401 then redirect it to somewhere else. – KKS May 16 '16 at 13:08
  • I tried it and didnt get it to work on the main router. If you can make it work please post a fiddle or similar! :) – Erik Svedin May 16 '16 at 15:06
1

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.

Walfrat
  • 5,363
  • 1
  • 16
  • 35