Elias
Elias

Reputation: 802

UI-router: understanding resolve promises flow of the root-sub states

I'm developing the angular app, using ui - router.

I created root state which is an abstract one aimed to resolve async. dependencies. So every sub states from my perspective should be able to use those dependencies in own resolve state properties. So if abstract root state resolving async dependencies and sub state also resolving asyc dependencies, the latter one should wait for the root dependencies to resolved, before starting its own resolve method. Right?

Here is the code example, that shows what I mean:

async, promise based methods that are used inside corresponding resolve

 public iAmInTheRootState(): IPromise<any> {
    let deferred = this._$q.defer();

    this._$timeout(() => {
        deferred.resolve();
    }, 3000);

    return <IPromise<any>> deferred.promise;
}

public iAmInTheSubState(): IPromise<any> {
    let deferred = this._$q.defer();

    this._$timeout(() => {
        deferred.resolve();
    }, 100);

    return <IPromise<any>> deferred.promise;
}

Root abstract state:

$stateProvider
    .state('app', {
        url: '/',
        abstract: true,
        templateUrl: 'layout/app-view.html',
        resolve: {
            auth: function (Auth: IAuthService) {
                'ngInject';
                return Auth.iAmInTheRootState().then(() => {
                    console.log('I am the root state, so I should be first');
                });
            }
        }
    });

Sub state which is the daughter state:

$stateProvider.state('app.my-calls', {
    url: '',
    controller: 'MyCallsController',
    controllerAs: '$ctrl',
    templateUrl: 'states/my-calls/my-calls.html',
    resolve: {
        secondAuth: (Auth: IAuthService) => {
            'ngInject';
            return Auth.iAmInTheSubState().then( () => {
                console.log('although I am faster I should be second because i am in the sub state');
            });
        }
    }
})

But the output differs from my expectations:

enter image description here

Upvotes: 0

Views: 266

Answers (2)

Elias
Elias

Reputation: 802

Although Tim's answer can be considered as a direct response to my question, one may want to understand how to make sub states to wait for their parent's resolve method.

Check out the github issue regarding to this topic.

So in short: sub states should have parent state as a dependency:

Parent State:

$stateProvider
.state('app', {
    url: '/',
    abstract: true,
    templateUrl: 'layout/app-view.html',
    resolve: {
        ParentAuth: function (Auth: IAuthService) {
            'ngInject';
            return Auth.iAmInTheRootState().then(() => {
                console.log('I am the root state, so I should be first');
            });
        }
    }
});

Child state:

$stateProvider.state('app.my-calls', {
url: '',
controller: 'MyCallsController',
controllerAs: '$ctrl',
templateUrl: 'states/my-calls/my-calls.html',
resolve: {
    SubAuth: (ParentAuth, Auth: IAuthService) => {
        'ngInject';
        return Auth.iAmInTheSubState().then( () => {
            console.log('although I am faster I should be second because i am in the sub state');
        });
    }
 }
 })

Upvotes: 0

Tim
Tim

Reputation: 151

In your example, the 'app.my-calls' is indeed created after the 'app' state (you can verify this by logging the onEnter callback.)

From Ui-router wiki

Resolve

You can use resolve to provide your controller with content or data that is custom to the state. resolve is an optional map of dependencies which should be injected into the controller.

If any of these dependencies are promises, they will be resolved and converted to a value before the controller is instantiated and the $stateChangeSuccess event is fired.

The resolve callback is not used to delay the state creation, but used to delay the controler creation.

To understand the full flow, you can log the $stateChangeStart & $stateChangeSuccess events.

Upvotes: 2

Related Questions