web-dev-qa-db-de.com

Angular JS ui-router, wie von einem übergeordneten zu einem untergeordneten Status umgeleitet werden?

Bei Verwendung von onEnter zur Umleitung in einen Status tritt eine Endlosschleife auf, wenn der neue Status dem aktuellen Status untergeordnet ist.

Beispiel:

$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'

Wann:

$state.go 'inventory.low'

Wird aufgerufen, wird der Zustand inventory neu initialisiert, sodass er erneut aufgerufen wird = Endlosschleife.

Wenn der Umleitungsstatus jedoch lautet:

$state.go 'otherStateThatIsNotAChild'

Dieses Problem tritt nicht auf. Ich gehe davon aus, dass der übergeordnete Zustand neu initialisiert wird, aber warum?

  1. Warum wird der übergeordnete Status erneut initialisiert, wenn .go für einen untergeordneten Status aufgerufen wird?
  2. Wie würden Sie dann die Umleitung in einen untergeordneten Zustand durchführen?
14
TaylorMac

1) Warum wird der übergeordnete Zustand erneut initialisiert, wenn .go für eine .__ aufgerufen wird. Kindzustand

Während ein Übergang ausgeführt wird, führt jeder Übergang von $ state.go/transitionTo dazu, dass der Übergang derzeit in Bearbeitung ersetzt wird. Ein in-Process-Übergang, der ersetzt wird, wird sofort abgebrochen. Da Ihr ursprünglicher Übergang zu inventory nicht abgeschlossen ist, wenn alle onEnters aller Zustände aufgerufen werden, wird der ursprüngliche Übergang abgebrochen und ein Übergang new zu inventory.low wird aus dem zuvor aktiven Zustand gestartet.

Siehe ui-router src https://github.com/angular-ui/ui-router/blob/master/src/state.js#L897 ... 

2) Wie würden Sie dann die Umleitung in einen untergeordneten Zustand durchführen?

Du könntest... 

  • Umschließen Sie $state.go in eine $timeout(), damit der ursprüngliche Übergang vor der Umleitung abgeschlossen werden kann.
  • Rufen Sie stattdessen $ state.go von Ihrem Controller aus auf. Der UI-Router ruft den Controller aus der Ui-View-Anweisung auf, NACHDEM der Übergang abgeschlossen ist.

Stellen Sie auf jeden Fall sicher, dass Ihre App so weitergeleitet werden soll. Wenn ein Benutzer direkt zu einem anderen untergeordneten Status des Inventars navigiert wurde (z. B. inventory.high), wird die Weiterleitung trotzdem durchgeführt, wodurch er zu inventory.low gezwungen wird. Dies wäre nicht der beabsichtigte Zweck.

20
Chris T

Ich hatte das gleiche Problem. Die einfache Lösung, die ich gefunden habe, besteht darin, Statusänderungen zu hören und von dort zu Ihrem untergeordneten Status umzuleiten.

Es ist eine Art Hack, bei dem Routen standardmäßig in untergeordnete Status umgeleitet werden. Es ist nicht erforderlich,url: '' oder $state.go() auszuführen, sie funktionieren nicht richtig.

Also in config Datei:

$rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState) {
  if (toState.redirectTo) {
    event.preventDefault();
    $state.go(toState.redirectTo, toParams);
  }
});

In state Datei:

.state('home', {
  url: '',
  redirectTo: 'home.list'
});

Ich habe ein Beispiel für Gist gemacht: https://Gist.github.com/nikoloza/4ab3a94a3c6511bb1dac

9
nikoloza

Sie müssen einen Schritt zurücktreten und darüber nachdenken, was Sie erreichen wollen. Was ist der Sinn eines Zustands, wenn er nur zu einem untergeordneten Zustand umleitet?

In Bezug auf Ihre erste Frage werden Elternzustände immer aktiviert, wenn Sie zu einem untergeordneten Status gelangen. Dieses Verhalten ist äußerst hilfreich, wenn Sie Daten zwischen den Status teilen möchten. Ohne sie wäre verschachteltes Routing unmöglich (oder wäre nicht sinnvoll).

Bei der zweiten Frage habe ich an einigen großen eckigen Apps gearbeitet, die ich bisher nicht brauchte.

OK, glauben Sie es oder nicht, so sehr ich es hasse, es zu sagen, gerade jetzt bin ich auf ein Szenario gestoßen, in dem ich dies tun musste. Ich habe eine profile/userName-Route (technisch sollte dies profile/userName/details sein) und eine profile/userName/products-Route. Ich wollte eine Master-Ansicht für beide Zustände haben, aber gleichzeitig wollte ich, dass die profile/userName-Route eine saubere URL hat, wie: profile/mike62, NICHT profile/mike62/details. So endete ich damit:

.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:/?.*}'})

Am Ende ist es so, aber es gibt viele Möglichkeiten:

in meinem publicProfile-Zustandscontroller (dies ist der Basiszustand):

$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);

Ja, es fühlt sich hackig an, aber jetzt weiß ich, dass es einige Edge-Fälle gibt, in denen wir dies tun möchten, obwohl wir unser Staatsdesign insgesamt überdenken könnten. Eine unansehnlichere Methode wäre, den aktuellen Status in einem $ timeout mit einer kleinen Verzögerung im Basisstatus zu überprüfen. Wenn wir uns nicht im publicProfile.products-Status befinden, navigieren wir zu unserem bevorzugten/de-facto-Status von publicProfile.details.

4

Es scheint, dass Sie versuchen, einen standardmäßigen untergeordneten Status festzulegen.

Dies ist eine häufig gestellte Frage zu ui-router: Gewusst wie: Einrichten eines standardmäßigen/untergeordneten Statusstatus

Tl; dr verwendet abstract states mit den Einstellungen abstract: true im übergeordneten Status. Wenn Sie mehrere untergeordnete Status hinzufügen, wird standardmäßig der erste untergeordnete Status verwendet.

$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'
    });
3
Atav32

Gemäß der Antwort von pdvorchik auf den bezogenen GitHub-Thread gibt es eine einfache Lösung für dieses Problem, die perfekt für mich funktionierte. Sie bestand darin, den Aufruf $state.go in einen Aufruf von .finally() einzuhüllen, um sicherzustellen, dass der erste Übergang abgeschlossen ist, bevor ein neuer gestartet wird.

onEnter: ['$state', function($state){
    if ($state.transition) {
        $state.transition.finally(function() {
            $state.go('home.my.other.state', {})
        });
    }
}]
2

Nach der Erklärung von @Chris T scheint es die sauberste Lösung zu sein, auf das $stateChangeSuccess-Ereignis zu achten:

redirects =
  inventory: 'inventory.low'

$rootScope.$on '$stateChangeSuccess', (e, toState) ->
  redirect = redirects[toState.name]
  $state.go redirect if redirect

Wo Weiterleitungen eventuelle Routenumleitungen enthalten. Danke euch allen!

1
TaylorMac

Machen Sie den niedrigen Lagerbestand nicht zu einem untergeordneten Status.

$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'
0
craigb