v0d1ch
v0d1ch

Reputation: 2748

Resolve promise in route resolve property

Is it possible to use promises right in the resolve property of the router? I want to use a service to check if user has permission to access the page in the route resolve. Example:

         $stateProvider
            .state('user', {
                url: '/user',
                templateUrl: 'app/views/user/views/user.html',
                controller: 'UserCtrl',
                controllerAs: 'vm',
                resolve: {
                    security: ['$state', 'userService','messageService','$location', function ($state, userService, messageService, $location) {
                        userService.checkUserAndRole("user").then(function(data){
                        var data = userService.checkUserAndRole("user");
                        if (data.access === false) {
                            messageService.errorMsg(data.message);
                            var route = data.route ? data.route:$location.path();
                            $state.go(route);
                        });
                    }]
                }
            });

I am using $q service in userService to return a promise

    function checkUserAndRole(page){
        var defer = $q.defer();
        var response = {};
        if(!ROLE[page]){
            response.access = false;
            response.message = "Page is not defined in ROLE config. Please tell admin to correct this!";
            response.route = "login";
            defer.resolve(response);
        }

        if(!user.roles){
            response.access = false;
            response.message = "Logged in user does not seem to have any role data defined. That is why you are redirected";
            response.route = "/";
            defer.resolve(response);
        }

        if(!helperService.arrayIntersect(ROLE[page],user.roles)){
            response.access = false;
            response.message = "You don't have a permission to access '/"+page+"' url";
            defer.resolve(response);
        }

        response.access = true;
        response.message = "";
        response.route = "";

        return defer.promise;
    }

If I don't use promises but return the data from the service immediately all works well but on refresh I still get invalid results since user data from service is fetched via $http and the data is not there yet when the route is resolved.

Upvotes: 0

Views: 1631

Answers (1)

georgeawg
georgeawg

Reputation: 48968

Resolve functions work with promises, but it is important to return the promise to the resolver function.

$stateProvider
    .state('user', {
        url: '/user',
        templateUrl: 'app/views/user/views/user.html',
        controller: 'UserCtrl',
        controllerAs: 'vm',
        resolve: {
            security: ['$state', 'userService','messageService','$location', function ($state, userService, messageService, $location) {
                //RETURN the promise
                return (
                    userService.checkUserAndRole("user")
                        .then(function(data){
                            if (data.access === false) {
                                messageService.errorMsg(data.message);
                                var route = data.route ? data.route:$location.path();
                                $state.go(route);
                        });
                );
            }]

        }
    });

By failing to return the promise to the resolver function, the router resolves security to null without waiting for the promise to complete.


Also the checkUserAndRole function fails to resolve the $q.defer in the case where none of the if conditions are true. The code needs to be rewritten.

function checkUserAndRole(page){
    var response = {};
    var promise = $q.when();
    if(!ROLE[page]){
        response.access = false;
        response.message = "Page is not defined in ROLE config. Please tell admin to correct this!";
        response.route = "login";
        promise = $q.when(response);
    } else if(!user.roles) {
        response.access = false;
        response.message = "Logged in user does not seem to have any role data defined. That is why you are redirected";
        response.route = "/";
        promise = $q.when(response);
    } else if(!helperService.arrayIntersect(ROLE[page],user.roles)){
        response.access = false;
        response.message = "You don't have a permission to access '/"+page+"' url";
        promise = $q.when(response);
    } else {
        response.access = true;
        response.message = "";
        response.route = "";
        promise = $q.when(response);
    };

    return promise;
}

Avoid $q.defer as it is prone to coding errors. Instead use $q.when to create a promise from a value.

Upvotes: 1

Related Questions