Lors de l'utilisation de onEnter pour rediriger vers un état, si le nouvel état est un enfant de l'état actuel, une boucle infinie se produit.
Exemple:
$stateProvider
.state 'inventory',
url: '/inventory'
templateUrl: 'views/inventory.html'
controller: 'InventoryCtrl'
onEnter: () ->
$state.go 'inventory.low'
.state 'inventory.low',
url: '/low'
templateUrl: 'views/inventory-table.html'
controller: 'LowInventoryCtrl'
Quand:
$state.go 'inventory.low'
Est appelé, l'état inventory
est réinitialisé, ce qui a pour effet de le rappeler = boucle infinie.
Cependant, si l'état de redirection est:
$state.go 'otherStateThatIsNotAChild'
Ce problème ne se produit pas. Je suppose que l'état parent est en cours de réinitialisation, mais pourquoi?
.go
est appelé dans un état enfant?1) Pourquoi l’état parent est-il réinitialisé lorsque .go est appelé sur un état de l'enfant?
Lors du processus de transition, tout $ state.go/transitionTo entraînera le remplacement de la transition actuellement en cours. Une transition en cours qui est remplacée est immédiatement annulée. Étant donné que votre transition d'origine vers inventory
n'est pas terminée au moment où tous les états 'onEnters sont appelés, la transition d'origine est annulée et une transition new vers inventory.low
est lancée à partir de l'état précédemment actif.
Voir ui-router src https://github.com/angular-ui/ui-router/blob/master/src/state.js#L897 ...
2) Comment alors géreriez-vous la redirection vers un état enfant?
Vous pourriez...
$state.go
dans une $timeout()
pour permettre à la transition d'origine de se terminer avant la redirection.Dans tous les cas, assurez-vous que votre application redirige de la sorte. Si un utilisateur accédait directement à un autre état d'inventaire de l'inventaire (tel que inventory.high
), la redirection se produirait toujours, le forçant à passer à inventory.low
, ce qui ne correspondrait pas à son intention.
J'ai eu le même problème. La solution simple que j'ai trouvée consiste à écouter les changements d'état et à les rediriger vers votre état enfant à partir de là.
C'est une sorte de hack qui fait que les routes redirigent vers des états enfants par défaut. Il n'est pas nécessaire de faireurl: ''
ou $state.go()
, ils ne fonctionnent pas correctement.
Donc, dans le fichier config
:
$rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState) {
if (toState.redirectTo) {
event.preventDefault();
$state.go(toState.redirectTo, toParams);
}
});
Dans le fichier state
:
.state('home', {
url: '',
redirectTo: 'home.list'
});
J'ai fait un exemple sur Gist: https://Gist.github.com/nikoloza/4ab3a94a3c6511bb1dac
Vous devez prendre du recul et réfléchir à ce que vous essayez d'accomplir. Quel est l'intérêt d'avoir un état alors qu'il ne fait que rediriger vers un état enfant?
En ce qui concerne votre première question, les états parents sont toujours activés lorsque vous arrivez à un état enfant. Ce comportement est extrêmement utile pour le partage de données entre états. Sans cela, le routage imbriqué serait impossible (ou plutôt n'aurait aucun sens).
En ce qui concerne la deuxième question, j'ai travaillé sur quelques grosses applications angulaires et je n'ai pas encore eu besoin de le faire.
OK, croyez-le ou non, autant que je déteste dire ça, je suis tombé sur un scénario dans lequel je devais le faire. J'ai un itinéraire profile/userName
(techniquement, il devrait s'agir de profile/userName/details
) et d'un itinéraire profile/userName/products
, je voulais avoir une vue principale pour les deux états, mais je voulais en même temps que l'itinéraire profile/userName
contienne une URL propre, comme: profile/mike62
, NOT profile/mike62/details
. Alors j'ai fini par faire ceci:
.state('publicProfile', { url: '/profile/{username}'})//this is the base/Shell state
.state('publicProfile.details',{url:null})//I want this to be my de-facto state, with a clean URL, hence the null url
.state('publicProfile.products', {url:'/products/{exceptProductId:/?.*}'})
En fin de compte, il y a plusieurs façons:
dans mon contrôleur d'état publicProfile
(il s'agit de l'état de base):
$scope.state = $state;
$scope.$watch('state.current', function(v) {
if(v.name=='publicProfile') {//want to navigate to the 'preferred' state if not going to publicProfile.products
$state.go('publicProfile.details');
};
}, true);
Oui, cela me semble hacky, mais je sais maintenant que nous voulons faire cela dans certains cas Edge, même si nous pourrions repenser notre conception de l'état. Une autre façon, plus sale, serait de vérifier l'état actuel dans un $ timeout avec un léger retard dans l'état de base. Si nous ne sommes pas dans l'état publicProfile.products
, nous passons à l'état préféré/de facto de publicProfile.details
.
On dirait que vous essayez de définir un état enfant par défaut.
Voici une question fréquemment posée à propos de ui-router: Comment: configurer un état enfant par défaut/index
Le tl; dr doit utiliser états abstraits selon les paramètres abstract: true
dans l’état parent. Si vous ajoutez plusieurs états enfants, il passera par défaut au premier état enfant.
$stateProvider
.state('inventory', {
abstract: true,
url: '/inventory',
templateUrl: 'views/inventory.html',
controller: 'InventoryCtrl'
}).
.state('inventory.low', {
url: '/low',
templateUrl: 'views/inventory-table.html',
controller: 'LowInventoryCtrl'
});
Selon la réponse de pdvorchik sur le fil de discussion GitHub associé , il existe un correctif simple qui a parfaitement fonctionné pour moi, consistant à encapsuler l'appel $state.go
dans un appel .finally()
pour s'assurer que la première transition se termine avant la nouvelle commencé.
onEnter: ['$state', function($state){
if ($state.transition) {
$state.transition.finally(function() {
$state.go('home.my.other.state', {})
});
}
}]
Après l'explication de @Chris T, il semble que la solution la plus propre consiste à écouter l'événement $stateChangeSuccess
:
redirects =
inventory: 'inventory.low'
$rootScope.$on '$stateChangeSuccess', (e, toState) ->
redirect = redirects[toState.name]
$state.go redirect if redirect
Où les redirections contiendront toutes les redirections de route pouvant survenir. Merci a tous!
Ne faites pas de l'état d'inventaire faible un état enfant.
$stateProvider
.state 'inventory',
url: '/inventory'
templateUrl: 'views/inventory.html'
controller: 'InventoryCtrl'
onEnter: () ->
$state.go 'lowinventory'
.state 'lowinventory',
url: '/inventory/low'
templateUrl: 'views/inventory-table.html'
controller: 'LowInventoryCtrl'