angularjsangular-ui-routerangular-ui-router-extras

Resolve a promise before state changes UI-Router


I am using ui-router and ui-router extras

$scope.$on('$transitionStart', function (e, $transition$) {
    var params = {id:1};
    factory.webService(params)
        .success(function (data) {
            if (data.d == "Expired") {//Inspects the application session
                //Stops the state request and sents you to the login screen
                e.preventDefault();
                location.href = "login.html";
            }
            else if (data.d == "NotAllowed") {
                //Stops the state request and keeps you on the state you already are
                e.preventDefault();
            } else {
                //Proceed to load the requested state
            }
        })
        .error(function (data, status) {
            alert(data.Message);
        });
});

I need to resolve the success part before the $stateChangeStart is loaded and can't figure out how to do it.

Is there any way of achieving this?

EDIT

So I have my code like this

.state('myState', {
        url: "/myState",
        templateUrl: 'views/template.html',
        controller: 'ctrlTemplate',
        viewId: 99,
        resolve: {
            fn: function ($stateParams, myFactory) {
                myFactory.checkSession({viewId:99})
                .then(function onSuccess(response) {
                    var data = response.data;
                    if (data.d == "Expired") {
                        throw "Expired";
                    }
                    else if (data.d == "NotAllowed") {
                        throw "NotAllowed";
                    } else {
                        //RETURN to chain data
                        return data;
                        //Proceed to load the requested state
                    }
                })
                .catch(function (response) {
                    //e.preventDefault();
                });
            }
        }
    })

The $http .then function is still resolving AFTER $stateChangeStart and $transitionStart happens and the new state is already loading. I can see it happening in the browser console debugger

Plz help.


Solution

  • i need to wait for the $http response and catch it on the .success function

    The .success method (deprecated and now removed from AngularJS 1.6) is not capable of rejecting a promise, but the .then method is capable of converting a success to a rejection:

    var promise = factory.webService(params)
        //.success(function (data) {
        .then( function onSuccess(response) {
            var data = response.data;
            if (data.d == "Expired") {
                //THROW to create a rejected promise
                throw "Expired";
                /*
                //Inspects the application session
                //Stops the state request and sents you to the login screen
                e.preventDefault();
                location.href = "login.html";
                */
            }
            else if (data.d == "NotAllowed") {
                //THROW to create a rejected promise
                throw "NotAllowed";
                /*
                //Stops the state request and keeps you on the state you already are
                e.preventDefault();
                */
            } else {
                //RETURN to chain data
                return data;
                //Proceed to load the requested state
            }
        })
        //.error(function onError(data, status) {
        .catch( function(response) {
            var data = response.data;
            var status = response.status;
            alert(data.Message);
            //THROW to chain rejection
            throw data.Message;
        });
    });
    

    By using either a return or throw statement, a new derived promise is created from the response resolved from either the .then or .catch methods of the httpPromise.

    When the resolver function of a ui-router state gets a rejected promise, the state change is aborted.


    UPDATE

    So I have my code like this ...

        //ERRONEOUS
        resolve: {
            fn: function ($stateParams, myFactory) {
                myFactory.checkSession({viewId:99})
                .then(function onSuccess(response) {
                    var data = response.data;
                    if (data.d == "Expired") {
                        throw "Expired";
                    }
    

    I can't understand why the throw doesn't trigger the $stateChangeError.

    The resolver function needs to return the promise to the router:

        resolve: {
            fn: function ($stateParams, myFactory) {
              //vvvvvv RETURN the promise
                return myFactory.checkSession({viewId:99})
                .then(function onSuccess(response) {
                    var data = response.data;
                    if (data.d == "Expired") {
                        throw "Expired";
                    }
    

    When a function omits a return statement, the function returns a value of undefined. In that case the router considers it a success with a value of undefined.

    Also the .catch method is erroneous

        //ERRONEOUS   
              })
                .catch(function (response) {
                    //e.preventDefault();
                });
    

    When the .catch method's rejection handler omits a return (or throw) statement, the function returns a value of undefined. This will convert the rejection to a successful promise that resolves with a value of undefined.

          //CORRECT   
                })
                .catch(function (response) {
                    //THROW to chain rejection
                    throw response;
                    //e.preventDefault();
                });
    

    The rule of thumb for functional programming is -- always return something.

    In the case of promise success and rejection: always return or throw something.