-
Notifications
You must be signed in to change notification settings - Fork 3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Due to fix in 0.2.13 .when('/parent', '/parent/child') is not working with ui-sref #1584
Comments
+1, I have the same issue. |
+1 As mentioned, this prevents redirecting URLs/states when using ui-sref. This is unfortunate as it now means I cannot upgrade past 0.2.12 since I require this functionality. I don't think we should have to clutter our $stateChangeStart event just to handle this, especially if we have multiple URL/state redirects in place. Hopefully this issue can be fixed in 0.2.14. |
+2 http://plnkr.co/edit/xyVPZePL2eV0lp8HxPBG?p=preview it works with 0.2.12 but nOT with 0.2.13. If the new behaviour is intended I would like to know what is the recommended way for setting default states. |
People who are using .when this way: do you really want "state-based redirection"? .when is for matching a url and redirecting, and/or doing some other action. If this issue affects you, it means you are navigating using states, not url. I think what you are doing is navigating to a state (using state.go/ui-sref), but you really want to navigate to some other state (usually a substate). In UI-Router the state is the primary focus, and the URL that maps to that state is secondary. In my eyes a state-based redirect is preferable to a url based one. Url redirection is more very loosely coupled and feels sloppy when compared to state-based redirection. Read my analysis of the "double transition" problem here: #1573. As noted in the issue, avoiding re-sync is a safety net to avoid double-transitions. However, the primary fix is to ensure that all state-to-url mappings are bi-directional. As such, since state based redirection using $stateChangeStart in 0.2.x is rather clumsy, I'm going to comment out the "avoid resync" logic for 0.2.14. I'm going to reintroduce it in 1.0 when we have a better state-level redirection API. If there are any other use cases that aren't "state-level redirection", please inform me so I make sure to account for them. |
+1 same problem As for the use case: Better (i.e. non-central) state forwarding would be very welcome, also using the urlRouterProvider based forward for what really should be a state forward. The parent state is just a placeholder for us (which when called should redirect to a default child state) and we reference it in ui-sref to get "automagically" correct setting of the active class in the navigation. Since we have this use case in many places of our app we do not want to clutter the $stateChangeStart function with state redirects and thus opted to use $urlRouterProvider. |
👍 Same issue! |
For me, the reason I navigate to a parent state rather than its substate directly is because of For example, I have a side menu item associated with, let's say,
When I click on Settings link, I want it to redirect me to the first substate (i.e. Maybe there is a better way of using So if I want to keep If there is a better way to handle the case above, I'd like to know as well. Thanks. |
@yaru22 +1 Exactly the same scenario here! |
+1 Same issue |
+1 |
+1 |
👍 I have the same issue, I use it like @yaru22 for ui-sref-active and for defining the default subroute with ui-router-extras deep state redirect |
+1 |
@christopherthielen another use case besides the one presented by @yaru22 The 2nd state I need that the profile state clickable on the index page loads the profile view and redirects to the default nested state which is Here's a working plunker with what I got so far, with the fix mentioned by @Radim-Kohler. |
👍 |
4 similar comments
+1 |
👍 |
+1 |
+1 |
In my case the underlying issue is in fact a feature request: we need a way to specify default child states. Given: $stateProvider
.state("settings", {
abstract: true,
url: "/settings"
})
.state("settings.general", {
url: "/general"
})
.state("settings.notifications", {
url: "/notifications"
})
.state("settings.advanced", {
url: "/advanced"
}) It would be nice if we could say something like: .state("settings.general", {
url: "/general",
isDefaultChild: true
}) So that when we navigate to the abstract parent state: $state.go("settings") We would get redirected (or even better: go directly) to I used to do this using What do you think? Thanks |
Also one more thing to be aware of when using the fix mentioned by @Radim-Kohler is that the order of defining the data object in the state change so when using this solution the child state's data gets defined before the parent state's data so the parent overwrite the child's data (a more detailed explanation could be found in this bug report) so if you'r using something that require you to pass data to the child state with the data attribute in the state definition object you should be aware of this bug, for me I think the best solution here is to define all the data for children in the parent state until this gets fixed. |
@yahyaKacem I agree that @gabrielmaldi suggestion should be in the parent state and would like to expand on that idea by adding another option |
I didn't really give the proposed syntax a lot of thought, just wanted to outline the idea of default child states. Perhaps it would be better to have it in the parent state, or maybe someone comes up with a better way. @cs-NET I think you should look into UI-Router Extras which has |
+1 |
+1 |
@yahyaKacem like these redirections defined on the parent - much cleaner than have it outside à-la-urlRouterProvider.
Which That
Not so clean anymore... What do you think? |
@getvega I'm actually doing that differently, I just inject the posts service into the run function then get the posts[0].id from there and form the parameters object in the run function then when I call
as for the |
@yahyaKacem This may make your code a little more reusable. This is how we handle redirects. .config(['$stateProvider', '$urlRouterProvider',function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('Account.Security', {
url: '/security',
redirectTo: 'Account.Security.ChangePassword' // OR
//redirectTo: function($state){ $state.go('Account.Security.ChangePassword', { id: 'some id' }); }
});
});
.run(['$rootScope', '$state', function($rootScope, $state){
$rootScope.$on('$stateChangeStart', function(event, toState) {
var redirect = toState.redirectTo;
if (redirect) {
event.preventDefault();
if(angular.isFunction(redirect))
redirect.call($state);
else
$state.go(redirect);
}
});
}]); The redirectTo function could do whatever logic you needed. Ex. get the id for the last item the user was viewing. |
@getvega perhaps the redirection should just forward any parameters it got (e.g. from |
@gabrielmaldi @acollard @yahyaKacem @getvega Great conversation. Let's move the discussion to #1235 to keep this one about url-based redirects |
For 0.2.13, here is a very simple recipe you can use to redirect to a default substate:
See it in action in a fork of the above plunkr that @yahyaKacem posted: |
How can I gain access to resolved items in my redirectTo function? It would make sense that id need to resolve an item from the database and the state of that item could effect what params or states I need to redirect to. |
Accessing parent resolves is a little difficult, the process of running the resolves happens inside the $state.transitionTo and are not accessible through the event. Accessing the toStates resolves will likely not be possible at all, since redirectTo is processed before the toStates resolves are run. But if you can use a registered service instead of a parent resolve then below will work for you. We've changed our method above to support dependency injection. My code above has been changed to: module.run(['$rootScope', '$state', '$injector', function ($rootScope, $state, $injector) {
$rootScope.$on('$stateChangeStart',function (event, toState, toParams) {
var redirect = toState.redirectTo;
if (redirect) {
if (angular.isString(redirect)) {
event.preventDefault();
$state.go(redirect, toParams);
}
else {
var newState = $injector.invoke(redirect, null, { toState: toState, toParams: toParams });
if (newState) {
if (angular.isString(newState)) {
event.preventDefault();
$state.go(newState);
}
else if (newState.state) {
event.preventDefault();
$state.go(newState.state, newState.params);
}
}
}
}
}); Here is a sample of using it: .config(['$stateProvider', '$urlRouterProvider',function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('Account.Security', {
url: '/security',
redirectTo: ['auth', function(auth){ return auth.isLicensed() ? { state: 'Account.Security.ChangePassword', params: 'some id' } : 'AccessDenied';}]
});
}); The ui-router team is looking to add this functionality natively #1235. My guess is parent resolves will still be unavailable. RedirectTo will likely occur before any resolves are run. Thoughts @christopherthielen? |
How would you handle redirectTo if auth.isLicensed() returned a promise instead of a bool? |
I'm assuming the promise from your redirectTo would still return one of the two options: the state name, or { state, param } object. I would probably change the listener to this to support promises: module.run(['$rootScope', '$state', '$injector', '$q', function($rootScope, $state, $injector, $q) {
$rootScope.$on('$stateChangeStart', function(event, toState, toParams) {
var redirect = toState.redirectTo;
if (redirect) {
if (angular.isString(redirect)) {
event.preventDefault();
$state.go(redirect, toParams);
} else {
var newState = $injector.invoke(redirect, null, {
toState: toState,
toParams: toParams
});
if (newState) {
event.preventDefault();
$q.when(newState).then(function(result) {
if (angular.isString(result)) {
$state.go(result);
} else if (result.state) {
$state.go(result.state, result.params);
}
});
}
}
}
}); RedirectTo would look like this: redirectTo: ['auth', function(auth){ return auth.isLicensed().then(function(isLoggedIn){ return isLoggedIn ? { state: 'Account.Security.ChangePassword', params: 'some id' } : 'AccessDenied';});}] Alternatively you could use a resolve to do the redirect. We do this as well, which already supports promises and injecting resolves (parents and self). $stateProvider.state({
name: 'home',
url: '/home',
resolve: {
loginRequired: ['$q','$state','auth', function($q, $state, auth){
return auth.isLoggedIn()
.then(function(isLoggedIn){
if(!isLoggedIn)
{
$state.go('login');
}
}
}]
}}); |
What do you think of:
That way it handles $promise objects (if using $resource you will need to return $promise probably. not sure) |
My option above supports promises, strings, and objects. $q.when() is used to wrap all three options and return a promise. If a non-promise is wrapped then it will run then() immediately. Checking for a promise yourself may not work well, I find it is easier to leave it up to angular. |
Has this been resolved yet? I implemented @christopherthielen 's solution for now (thanks for that!), but it would be great if this got fixed. |
@gwin003 it will be fixed with the next MAJOR release of ui-router. Almost everything will be different... |
Thanks @LordWingZero |
I'm liking your solution, but I added some code to support object literals like //Redirect to string
} else if (redirect.state) {
event.preventDefault();
$state.go(redirect.state, redirect.params);
} else {
//Redirect to injectable |
This missed 0.2.14, sorry. I'll get this into 0.2.15 ASAP |
UI-Router 1.0 preview is available: https://github.com/angular-ui/ui-router/tree/feature-1.0 |
I admire that... you are magicians. I have to touch it, start to test... upgrade... The idea about migrating to Typescript is MUST. We are on Typescript for 1,5 year... cannot imagine JS development without it anymore. Looking forward experience with the mighty 1.0 ;) |
I would say, that the _"fix"_ in 0.2.13, related to: Avoid re-synchronizing from url after .transitionTo, change: 19715d15 introduced a new issue. Or at least, the
.when('/parent', '/parent/child')
is not working forui-sref="parent"
Description of the "issue"
As documented here: How to: Set up a default/index child state small extract:
NOTE: in the above cite, I replaced the
home
in url withparent
to make it more consistentThis has stopped working. There are plunkers:
Examples
There are examples of the issue with different versions:
this is the change applied in "roll back" of a fix 19715d15 0.2.13 inside of the released angular-ui-router.js
Workaround without change of 0.2.13
I described how to go around here Angular UI-Router $urlRouterProvider .when not working when I click
<a ui-sref=“…”>
, we need eventing:Maybe this is the way to go (or please, give me better). But as mentioned below, not sure about consistency...
Question, is it an issue? or not
I can imagine that this issue would be refuesd as proper behaviour.
BUT, is it really what we want - if this will result in 2 different states:
The text was updated successfully, but these errors were encountered: