Angular - UI-Router erhält vorherigen Status


152

Gibt es eine Möglichkeit, den vorherigen Status des aktuellen Status abzurufen?

Zum Beispiel möchte ich wissen, was der vorherige Zustand vor dem aktuellen Zustand B war (wobei der vorherige Zustand Zustand A gewesen wäre).

Ich kann es nicht auf den Github-Dokumentenseiten des UI-Routers finden.


Die Antwort unten ist richtig. Sie können auch alle Informationen, die Sie benötigen, aus der Quelle finden, wenn die Dokumente nicht ausreichen. Hilfe goo.gl/9B9bH
David Chase

Antworten:


133

Der UI-Router verfolgt den vorherigen Status nach dem Übergang nicht mehr, aber das Ereignis $stateChangeSuccesswird gesendet, $rootScopewenn sich der Status ändert.

Sie sollten in der Lage sein, den vorherigen Status von diesem Ereignis zu erfassen ( fromist der Status, den Sie verlassen):

$rootScope.$on('$stateChangeSuccess', function (ev, to, toParams, from, fromParams) {
   //assign the "from" parameter to something
});

3
Was wäre ein Beispiel mit "von"?
Paul

Hier bedeutet "to" und "from" "toState" und "fromState". Angenommen, Ihre vorherige URL lautet localhost: xxxx / employee und controller ist 'EmployeesController'. Das Beispiel für 'fromState' lautet dann: Object {url: "/ employee", templateUrl: "/ employee", controller: "EmployeesController", name: " Mitarbeiter "}
Ranadheer Reddy

3
Ich habe dies in meinem Abstract getan: `$ rootScope.previousState; $ rootScope.currentState; $ rootScope. $ on ('$ stateChangeSuccess', Funktion (ev, to, toParams, from, fromParams) {$ rootScope.previousState = from.name; $ rootScope.currentState = to.name; console.log ('Vorheriger Status: '+ $ rootScope.previousState) console.log (' Aktueller Status: '+ $ rootScope.currentState)}); `Es verfolgt die vorherigen und aktuellen in rootScope. Ziemlich praktisch!
Federico

Die Verwendung der Lösung von @ endy-tjahjono ( stackoverflow.com/a/25945003/2837519 ) ist mehr inline von UI -Router 1.x.
Peter Ahlers

Ich liebe einen einfachen Weg mit history.back () <a href="javascript:history.back()" class="btn btn-default no-radius">
Serip88

146

Ich verwende Entschlossenheit, um die aktuellen Statusdaten zu speichern, bevor ich in den neuen Status wechsle:

angular.module('MyModule')
.config(['$stateProvider', function ($stateProvider) {
    $stateProvider
        .state('mystate', {
            templateUrl: 'mytemplate.html',
            controller: ["PreviousState", function (PreviousState) {
                if (PreviousState.Name == "mystate") {
                    // ...
                }
            }],
            resolve: {
                PreviousState: ["$state", function ($state) {
                    var currentStateData = {
                        Name: $state.current.name,
                        Params: $state.params,
                        URL: $state.href($state.current.name, $state.params)
                    };
                    return currentStateData;
                }]
            }
        });
}]);

8
viel besser als damit umzugehen$stateChangeSuccess
SET

10
Meiner Meinung nach sollte dies die akzeptierte Antwort sein. Obwohl dies $stateChangeSuccessfunktioniert, geschieht dies auf globaler Ebene, was die meiste Zeit nicht wirklich benötigt wird.
Pierre Spring

2
Genau. Das ist viel sauberer. Wenn Sie mit vielen Modulen arbeiten, die alle Status haben, wird $ stateChangeSuccess für jede Statusänderung ausgelöst, nicht nur für die in Ihrem Modul. Eine weitere Abstimmung dafür ist die bessere Lösung.
Jason Buchanan

1
@ Nissassin17 Welche Version benutzt du? In v0.2.15 wird beim direkten Öffnen eines Status das $state.currentObjekt is : {name: "", url: "^", views: null, abstract: true}.
Endy Tjahjono

3
Zu Ihrer Information - Ich musste Folgendes tun: Params: angle.copy ($ state.params) - Erläuterung: Ich verwende UI-Router v 1.0.0-beta 2 und hatte Probleme mit dem Params-Teil dieses Codes. Da $ state.params ein Objekt ist, wird es nach dem Auflösen der Funktion auf die aktuelle Ansicht aktualisiert. Ich musste dies in der Auflösungsfunktion tun, um zu verhindern, dass das Objekt in der aktuellen Ansicht aktualisiert wird.
Joe H

100

Aus Gründen der Lesbarkeit werde ich meine Lösung (basierend auf der Antwort von stu.salsbury) hier platzieren.

Fügen Sie diesen Code zur abstrakten Vorlage Ihrer App hinzu, damit er auf jeder Seite ausgeführt wird.

$rootScope.previousState;
$rootScope.currentState;
$rootScope.$on('$stateChangeSuccess', function(ev, to, toParams, from, fromParams) {
    $rootScope.previousState = from.name;
    $rootScope.currentState = to.name;
    console.log('Previous state:'+$rootScope.previousState)
    console.log('Current state:'+$rootScope.currentState)
});

Verfolgt die Änderungen in rootScope. Es ist ziemlich praktisch.


19
Es lohnt sich auch, die fromParams zu speichern, wenn Sie jemanden wirklich dorthin zurückleiten möchten, wo er herkommt.
Yaron

Ich liebe das und implementiere es in meinen Anwendungen. Danke
Fallenreaper

Wirklich nützlich ... Noch mehr mit localStorage können Sie den vorherigen Status überall abrufen.
Georgeos

14

Im folgenden Beispiel habe ich eine erstellt decorator(wird in der Konfigurationsphase nur einmal pro App ausgeführt) und dem $stateService eine zusätzliche Eigenschaft hinzugefügt, sodass bei diesem Ansatz keine globalen Variablen $rootscopezu anderen Diensten hinzugefügt werden und keine zusätzliche Abhängigkeit zu anderen Diensten als erforderlich ist $state.

In meinem Beispiel musste ich einen Benutzer auf die Indexseite umleiten, wenn er bereits angemeldet war und wenn er ihn nach der Anmeldung nicht auf die vorherige "geschützte" Seite umleiten sollte.

Die einzigen unbekannten Dienste (für Sie), die ich benutze, sind authenticationFactoryund appSettings:

  • authenticationFactoryverwaltet nur die Benutzeranmeldung. In diesem Fall verwende ich nur eine Methode, um festzustellen, ob der Benutzer angemeldet ist oder nicht.
  • appSettingssind Konstanten, nur um nicht überall Strings zu verwenden. appSettings.states.loginund appSettings.states.registerenthalten den Namen des Staates für die Anmelde- und Registrierungs-URL.

Dann müssen Sie in jedem controller/ serviceetc $stateService injizieren und können auf die aktuelle und vorherige URL wie folgt zugreifen:

  • Aktuell: $state.current.name
  • Bisherige: $state.previous.route.name

Über die Chrome-Konsole:

var injector = angular.element(document.body).injector();
var $state = injector.get("$state");
$state.current.name;
$state.previous.route.name;

Implementierung:

(Ich benutze angular-ui-router v0.2.17und angularjs v1.4.9)

(function(angular) {
    "use strict";

    function $stateDecorator($delegate, $injector, $rootScope, appSettings) {
        function decorated$State() {
            var $state = $delegate;
            $state.previous = undefined;
            $rootScope.$on("$stateChangeSuccess", function (ev, to, toParams, from, fromParams) {
                $state.previous = { route: from, routeParams: fromParams }
            });

            $rootScope.$on("$stateChangeStart", function (event, toState/*, toParams, fromState, fromParams*/) {
                var authenticationFactory = $injector.get("authenticationFactory");
                if ((toState.name === appSettings.states.login || toState.name === appSettings.states.register) && authenticationFactory.isUserLoggedIn()) {
                    event.preventDefault();
                    $state.go(appSettings.states.index);
                }
            });

            return $state;
        }

        return decorated$State();
    }

    $stateDecorator.$inject = ["$delegate", "$injector", "$rootScope", "appSettings"];

    angular
        .module("app.core")
        .decorator("$state", $stateDecorator);
})(angular);

12

Fügen Sie $ state auf $ stateChangeStart eine neue Eigenschaft mit dem Namen {previous} hinzu

$rootScope.$on( '$stateChangeStart', ( event, to, toParams, from, fromParams ) => {
    // Add {fromParams} to {from}
    from.params = fromParams;

    // Assign {from} to {previous} in $state
    $state.previous = from;
    ...
}

Jetzt können Sie überall dort, wo Sie möchten, $ state verwenden, den Sie zuvor zur Verfügung haben

previous:Object
    name:"route name"
    params:Object
        someParam:"someValue"
    resolve:Object
    template:"route template"
    url:"/route path/:someParam"

Und benutze es so:

$state.go( $state.previous.name, $state.previous.params );

9

Ich stecke mit dem gleichen Problem fest und finde den einfachsten Weg, dies zu tun ...

//Html
<button type="button" onclick="history.back()">Back</button>

ODER

//Html
<button type="button" ng-click="goBack()">Back</button>

//JS
$scope.goBack = function() {
  window.history.back();
};

(Wenn Sie möchten, dass es besser testbar ist, fügen Sie den $ window-Dienst in Ihren Controller ein und verwenden Sie $ window.history.back ()).


Es ist komplizierter, wenn Sie Menü in Ihrer Anwendung haben
Swanand

Wenn

Sie verlieren $ stateParams, wenn Sie es nicht in die URL verschieben. Die bessere Lösung besteht darin, Ihre vorherigen Parameter in $ rootScope zu speichern. Sie können es daraus abrufen und in den $ -Status verschieben, um zurückzukehren.
dangquang1020

Diese Antwort ist für mich am einfachsten zu implementieren.
Lalnuntluanga Chhakchhuak

8

Ich benutze einen ähnlichen Ansatz wie Endy Tjahjono.

Ich speichere den Wert des aktuellen Status, bevor ich einen Übergang vornehme. Sehen wir uns ein Beispiel an. Stellen Sie sich dies in einer Funktion vor, die ausgeführt wird, wenn Sie auf das klicken, was den Übergang auslöst:

$state.go( 'state-whatever', { previousState : { name : $state.current.name } }, {} );

Der Schlüssel hier ist das params-Objekt (eine Karte der Parameter, die an den Status gesendet werden) -> { previousState : { name : $state.current.name } }

Hinweis: Beachten Sie, dass ich nur das Namensattribut des $ state-Objekts "speichere", da dies das einzige ist, was ich zum Speichern des Status benötige. Aber wir könnten das ganze Staatsobjekt haben.

Geben Sie dann "was auch immer" wie folgt definiert an:

.state( 'user-edit', {
  url : 'whatever'
  templateUrl : 'whatever',
  controller: 'whateverController as whateverController',
  params : {
    previousState: null,
  }
});

Hier ist der Schlüsselpunkt das params-Objekt.

params : {
  previousState: null,
}

In diesem Zustand können wir dann den vorherigen Zustand wie folgt erhalten:

$state.params.previousState.name

6

Hier ist eine wirklich elegante Lösung von Chris Thielen UI-Router-Extras: $ previousState

var previous = $previousState.get(); //Gets a reference to the previous state.

previousist ein Objekt, das so aussieht: { state: fromState, params: fromParams }wobei fromState der vorherige Status und fromParams die vorherigen Statusparameter sind.


1
Am 16. Mai 2017 fügte Chris Thielen eine End-of-Life-Mitteilung für das Projekt hinzu: UI-Router-Extras.
Michael R

4

Ok, ich weiß, dass ich zu spät zur Party komme, aber ich bin neu in Angular. Ich versuche, dies hier in den John Papa Style Guide zu integrieren . Ich wollte dies wiederverwendbar machen, also habe ich es in einem Block erstellt. Folgendes habe ich mir ausgedacht:

previousStateProvider

(function () {
'use strict';

angular.module('blocks.previousState')
       .provider('previousState', previousStateProvider);

previousStateProvider.$inject = ['$rootScopeProvider'];

function previousStateProvider($rootScopeProvider) {
    this.$get = PreviousState;

    PreviousState.$inject = ['$rootScope'];

    /* @ngInject */
    function PreviousState($rootScope) {
        $rootScope.previousParms;
        $rootScope.previousState;
        $rootScope.currentState;

        $rootScope.$on('$stateChangeSuccess', function (ev, to, toParams, from, fromParams) {
            $rootScope.previousParms = fromParams;
            $rootScope.previousState = from.name;
            $rootScope.currentState = to.name;
        });
    }
}
})();

Kern Modul

(function () {
'use strict';

angular.module('myApp.Core', [
    // Angular modules 
    'ngMessages',
    'ngResource',

    // Custom modules 
    'blocks.previousState',
    'blocks.router'

    // 3rd Party Modules
]);
})();

core.config

(function () {
'use strict';

var core = angular.module('myApp.Core');

core.run(appRun);

function appRun(previousState) {
    // do nothing. just instantiating the state handler
}
})();

Jede Kritik an diesem Code hilft mir nur. Lassen Sie mich wissen, wo ich diesen Code verbessern kann.


2

Wenn Sie diese Funktionalität nur benötigen und in mehr als einem Controller verwenden möchten, ist dies ein einfacher Dienst zum Verfolgen des Routenverlaufs:

  (function () {
  'use strict';

  angular
    .module('core')
    .factory('RouterTracker', RouterTracker);

  function RouterTracker($rootScope) {

    var routeHistory = [];
    var service = {
      getRouteHistory: getRouteHistory
    };

    $rootScope.$on('$stateChangeSuccess', function (ev, to, toParams, from, fromParams) {
      routeHistory.push({route: from, routeParams: fromParams});
    });

    function getRouteHistory() {
      return routeHistory;
    }

    return service;
  }
})();

Dabei ist der 'Kern' in .module ('Kern') der Name Ihrer App / Ihres Moduls. Benötigen Sie den Dienst als Abhängigkeit von Ihrem Controller, dann können Sie in Ihrem Controller Folgendes tun:$scope.routeHistory = RouterTracker.getRouteHistory()


4
Wenn ich auf meiner Seite eine Navigationsschaltfläche hätte, die zum vorherigen Status im Verlauf wechselt, wird nach dem Navigieren zu diesem vorherigen Status stateChangeSuccess ausgelöst, der den Verlauf hinzufügt. Würde dieser Code nicht zu einer endlosen Schleife des Hin- und Hergehens zwischen zwei Seiten führen?
whatsTheDiff

1

Ich verfolge frühere Zustände in $ rootScope , sodass ich bei Bedarf einfach die folgende Codezeile aufrufe .

$state.go($rootScope.previousState);

In App.js :

$rootScope.$on('$stateChangeSuccess', function(event, to, toParams, from, fromParams) {
  $rootScope.previousState = from.name;
});

1
Ich denke, es ist besser, wenn Sie nicht nur von .name speichern. Auf diese Weise haben Sie auch die Parameter, falls Sie etwas wie $ state.go ($ tootScope.previousState.name, $ tootScope.previousState.params) ausführen müssen;
Abelabbesnabi

0

Für UI-Router (> = 1.0) sind StateChange-Ereignisse veraltet. Klicken Sie hier, um eine vollständige Migrationsanleitung zu erhalten

So rufen Sie den vorherigen Status des aktuellen Status in UI-Router 1.0+ ab:

app.run(function ($transitions) {
    $transitions.onSuccess({}, function (trans) {
         // previous state and paramaters
         var previousState = trans.from().name;
         var previousStateParameters = trans.params('from');
    });
});

-1

Eine wirklich einfache Lösung besteht darin, die Zeichenfolge $ state.current.name zu bearbeiten und alles auszuschneiden, einschließlich und nach dem letzten '.' - Sie erhalten den Namen des übergeordneten Staates. Dies funktioniert nicht, wenn Sie häufig zwischen Status wechseln, da nur der aktuelle Pfad zurückgemeldet wird. Aber wenn Ihre Zustände dem entsprechen, wo Sie sich tatsächlich befinden, funktioniert dies.

var previousState = $state.current.name.substring(0, $state.current.name.lastIndexOf('.'))
$state.go(previousState)

1
$state.go('^')wird dies erreichen
James

Und was ist mit den staatlichen Parametern?
Tushar Shukla

-2

Sie können den Status folgendermaßen zurückgeben:

$state.go($state.$current.parent.self.name, $state.params);

Ein Beispiel:

(function() {
    'use strict'

    angular.module('app')
        .run(Run);

    /* @ngInject */
    function Run($rootScope, $state) {

        $rootScope.back = function() {
            $state.go($state.$current.parent.self.name, $state.params);
        };

    };

})();

2
Dies ist keine gute Antwort, wenn Sie von einem nicht übergeordneten Status auf den Status zugreifen. Wenn Sie nur das übergeordnete Element des aktuellen Status möchten, erledigt es die Aufgabe.
Maxime Lafarie

1
Ist das nicht dasselbe wie die Verwendung von $ state.go ("^")?
Nathan Moinvaziri
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.