angular-ui / ui-router

The de-facto solution to flexible routing with nested views in AngularJS
http://ui-router.github.io/
MIT License
13.54k stars 3k forks source link

Consider redirectTo timing option? #3207

Closed Josh68 closed 7 years ago

Josh68 commented 7 years ago

I just struggled with doing a redirect after login, and finally realized I couldn't rely on a state's resolves to be satisfied before a redirectTo hook was triggered. I also played with using a promise in the redirectTo hook, but this got really messy (and didn't work).

I ended up having to use a variation on the "returnTo" resolve pattern from the sample app, but even there, i needed to use a promise with an interval calling a recursive function, waiting for a value from one of the state's other resolves to return (basically, the data for my entire application, which contains the instructions for routing, which are persisted in the db).

It seems to me that a state's redirectTo should be able to be conditioned to run (or complete) only when all of the current state's resolves have been satisfied. If there's a way to do this already that I'm missing, please give an example. I'm not sure whether there's a way in a redirectTo of using the injected transition to get a hold of the condition of the state's resolves.

Thanks for any feedback, and please close if there is a way of doing this that I'm missing.

christopherthielen commented 7 years ago

you might find the injector() on the transition useful.

resolve: { 
  something: () => getSomethingPromise(),
  somethingElse: (something) => getSomethingElsePromise(something),
  somethingElseElse: (somethingElse) => somethingElseElsePromise(somethingElse),
},
redirectTo: trans => 
    trans.injector().getAsync('somethingElseElse')
        .then(result => return getRedirect(result));

https://ui-router.github.io/docs/latest/interfaces/common.uiinjector.html#getasync

Josh68 commented 7 years ago

Aha, wish I'd asked this earlier. I will try that out and see if it does what I need. I also wasn't aware you could ladder your resolves like that. Thanks for your help.

As it is, I assume there must be a reason that the redirectTo doesn't automatically wait for all resolves to complete before triggering, but I'm not sure.

christopherthielen commented 7 years ago

As it is, I assume there must be a reason that the redirectTo doesn't automatically wait for all resolves to complete before triggering, but I'm not sure.

The idea is that the redirect can happen before any expensive resolves (or resolves that might fail) are fetched.

Consider this example:

.state({ 
  name: 'parent',
  resolve: { 
    spensive: (SpensiveApi) => SpensiveApi.get()
  }
})
.state({
  name: 'parent.child',
  resolve: {
    dataForChild: (ChildApi) => ChildApi.failsWhenUnauthenticated()
  },
  redirectTo: trans => {
    let AuthService = trans.injector().get('AuthService');
    if (!AuthService.isAuthenticated()) return 'login'
  }
})

By processing the redirect first, we avoid making an expensive API call. We also avoid the dataForChild resolve rejecting (due to HTTP 403, etc) when the user is unauthenticated.

If the resolves must be fetched to process the redirectTo we can either fetch them inside the redirectTo hook using the injector(), or we can declare the resolves as EAGER. The redirectTo hook is an onStart() hook at default priority (0). The EAGER resolves are also an onStart() hook, but are high priority (1000).

To specify the parent resolve as eager:

.state({ 
  name: 'parent',
  resolve: { 
    spensive: (SpensiveApi) => SpensiveApi.get()
  },
  resolvePolicy: {
    when: 'EAGER'
  }
})
Josh68 commented 7 years ago

Thanks for the details. I think my design (legacy, on which I'm doing a refactor with ui-router) is just a particular case. I was trying to think top-down with 1.5.x component architecture, and every component from the "master" parent on down depends on data that comes with authentication (it's sort of like a Redux single-store, but that's where the comparison and quality ends).

I did try using the EAGER resolvePolicy, but at least the way I'd architected things at that point in time, it didn't help. I still thing a redirectToPolicy could make sense, but the option to use getAsync in the redirectTo callback, and to inject resolves at that point, should work fine. I'm trying to implement that now and will report back. worked perfectly, so I'm closing this issue.

Josh68 commented 7 years ago

Not exactly still on topic, but if anyone would like to help contribute to docs with answers provided here in issues, is there a guide for this? Looks like a lot of auto-generated HTML in the meat of the documentation.

Regarding this and another issue, I would suggest putting many concrete examples in your answers somewhere around here:

Interface StateDeclaration ->
Injecting resolves into other things ->
During a transition, Resolve data can be injected into ...

I'd also link from the highest-level tutorial pages. Lots of concrete examples of how to inject resolves into other things, including other resolves and redirectTo methods (sync and async) would be very helpful.

christopherthielen commented 7 years ago

A guide for contributing to docs and website is a great idea!

The short version is:

the website is at https://github.com/ui-router/ui-router.github.io . It is a Jekyll site hosted on github pages.

The API docs are generated using Typedoc from the block comments in the source code.

Josh68 commented 7 years ago

Thanks. I will give a shot at a PR for something simple. I did fork that repo in order to try to contribute some edits, but wasn't sure how to proceed.

christopherthielen commented 7 years ago

I've added docs for all injectables: https://ui-router.github.io/ng1/docs/latest/modules/injectables.html

Also added a blurb to redirectTo about getAsync: https://ui-router.github.io/ng1/docs/latest/interfaces/ng1.ng1statedeclaration.html#redirectto

Josh68 commented 7 years ago

Thank you for the updates. The API is deep, and I know it's difficult to document everything well.

On Wed, Jan 11, 2017 at 2:26 PM, Chris Thielen notifications@github.com wrote:

I've added docs for all injectables: https://ui-router.github.io/ ng1/docs/latest/modules/injectables.html

Also added a blurb to redirectTo about getAsync: https://ui-router.github.io/ng1/docs/latest/interfaces/ ng1.ng1statedeclaration.html#redirectto

— You are receiving this because you modified the open/close state. Reply to this email directly, view it on GitHub https://github.com/angular-ui/ui-router/issues/3207#issuecomment-272015404, or mute the thread https://github.com/notifications/unsubscribe-auth/AAvFd8YVfqosSfcITOHnffaJ2A2kxGOqks5rRVcFgaJpZM4LOwfE .