Angular - router ui otrzymuje poprzedni stan

152

Czy istnieje sposób, aby uzyskać poprzedni stan obecnego stanu?

Na przykład chciałbym wiedzieć, jaki był poprzedni stan przed obecnym stanem B (gdzie poprzedni stan byłby stanem A).

Nie mogę go znaleźć na stronach doc github ui-routera.

Mihai H.
źródło
odpowiedź poniżej jest prawidłowa, możesz również znaleźć wszystkie potrzebne informacje ze źródła, jeśli dokumenty nie są wystarczające. help goo.gl/9B9bH
David Chase

Odpowiedzi:

133

ui-router nie śledzi poprzedniego stanu po przejściu, ale zdarzenie $stateChangeSuccessjest emitowane w $rootScopemomencie zmiany stanu.

Powinieneś być w stanie złapać poprzedni stan z tego wydarzenia ( fromjest to stan, z którego wychodzisz):

$rootScope.$on('$stateChangeSuccess', function (ev, to, toParams, from, fromParams) {
   //assign the "from" parameter to something
});
laurelnaiad
źródło
3
Jaki byłby przykład użycia „od”?
Paul,
Tutaj „do” i „od” oznacza „toState” i „fromState”. Weź pod uwagę, że Twój poprzedni adres URL to localhost: xxxx / worker, a kontroler to „EmployeesController”, a następnie przykład „fromState” to: Object {url: "/ workers", templateUrl: "/ workers", controller: "EmployeesController", name: " pracowników "}
Ranadheer Reddy
3
Zrobiłem to w moim streszczeniu: `$ rootScope.previousState; $ rootScope.currentState; $ rootScope. $ on ('$ stateChangeSuccess', function (ev, to, toParams, from, fromParams) {$ rootScope.previousState = from.name; $ rootScope.currentState = to.name; console.log ('Poprzedni stan: '+ $ rootScope.previousState) console.log (' Aktualny stan: '+ $ rootScope.currentState)}); `Śledzi poprzednie i aktualne informacje w programie rootScope. Bardzo przydatne!
Federico
Korzystanie z rozwiązania @ endy-tjahjono ( stackoverflow.com/a/25945003/2837519 ) jest bardziej wbudowane w interfejs ui-router 1.x.
Peter Ahlers,
Uwielbiam prosty sposób z history.back () <a href="javascript:history.back()" class="btn btn-default no-radius">
Serip88
146

Używam rozwiązania, aby zapisać dane bieżącego stanu przed przejściem do nowego stanu:

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;
                }]
            }
        });
}]);
Endy Tjahjono
źródło
8
znacznie lepsze niż radzenie sobie z$stateChangeSuccess
USTAW
10
Moim zdaniem powinna to być akceptowana odpowiedź. Mimo że $stateChangeSuccessdziała, robi to na poziomie globalnym, co nie jest potrzebne przez większość czasu.
Pierre Spring
2
Zgadzam się. To jest dużo czystsze. Jeśli masz do czynienia z wieloma modułami, z których wszystkie mają stany, $ stateChangeSuccess będzie uruchamiane przy każdej zmianie stanu, a nie tylko tych w Twoim module. Kolejny głos za lepszym rozwiązaniem.
Jason Buchanan
1
@ Nissassin17 której wersji używasz? W v0.2.15 podczas otwierania stan bezpośrednio ze $state.currentjest obiekt: {name: "", url: "^", views: null, abstract: true}.
Endy Tjahjono
3
Do Twojej wiadomości - musiałem wykonać następujące czynności: Params: angular.copy ($ state.params) - Wyjaśnienie: używam UI-Router v 1.0.0-beta 2 i miałem problemy z częścią Params tego kodu; ponieważ $ state.params jest obiektem, zaktualizuje się do bieżącego widoku po rozwiązaniu funkcji ... Musiałem to zrobić w funkcji rozwiązywania, aby zapobiec aktualizacji obiektu w bieżącym widoku.
Joe H
100

Ze względu na czytelność umieszczę tutaj moje rozwiązanie (oparte na anwser stu.salsbury).

Dodaj ten kod do abstrakcyjnego szablonu aplikacji, aby działał na każdej stronie.

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

Śledzi zmiany w rootScope. Jest całkiem poręczny.

Federico
źródło
19
Warto również przechowywać fromParams, jeśli naprawdę chcesz przekierować kogoś z powrotem tam, skąd przyszedł.
Yaron,
Uwielbiam to i wdrażam to w moich aplikacjach. Dzięki
Fallenreaper
Naprawdę przydatne ... Jeszcze bardziej, korzystając z localStorage, możesz uzyskać previousState w dowolnym miejscu.
georgeos
14

W poniższym przykładzie utworzyłem decorator(działa tylko raz na aplikację w fazie konfiguracji) i dodaje dodatkową właściwość do $stateusługi, więc to podejście nie dodaje zmiennych globalnych$rootscope do innych usług i nie wymaga dodawania żadnych dodatkowych zależności do innych usług niż $state.

W moim przykładzie musiałem przekierować użytkownika na stronę indeksową, gdy był już zalogowany i kiedy nie miał przekierowywać go na poprzednią „chronioną” stronę po zalogowaniu.

Nieznane tylko usługi (dla ciebie), które używam są authenticationFactoryi appSettings:

  • authenticationFactorypo prostu zarządza loginem użytkownika. W tym przypadku używam tylko metody identyfikacji, czy użytkownik jest zalogowany, czy nie.
  • appSettingssą stałymi tylko po to, aby nie używać wszędzie łańcuchów. appSettings.states.logini appSettings.states.registerzawierać nazwę stanu dla logowania i adresu URL rejestru.

Następnie w dowolnym controller/ serviceetc musisz wstrzyknąć $stateusługę i możesz uzyskać dostęp do bieżącego i poprzedniego adresu URL w następujący sposób:

  • Obecny: $state.current.name
  • Poprzedni: $state.previous.route.name

Z konsoli Chrome:

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

Realizacja:

(Używam angular-ui-router v0.2.17i 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);
CodeArtist
źródło
12

Dodaj nową właściwość o nazwie {previous} do $ state na $ stateChangeStart

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

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

Teraz gdziekolwiek potrzebujesz, możesz użyć $ state, będziesz mieć poprzednią dostępną

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

I używaj go w ten sposób:

$state.go( $state.previous.name, $state.previous.params );
Luis
źródło
9

Utknąłem z tym samym problemem i znajduję najłatwiejszy sposób, aby to zrobić ...

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

LUB

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

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

(Jeśli chcesz, aby było bardziej testowalne, wstrzyknij usługę $ window do kontrolera i użyj $ window.history.back ()).

NiRmaL
źródło
sprawa jest bardziej skomplikowana, gdy w aplikacji jest menu
Swanand
nie otrzymujesz swojego problemu, podaj więcej szczegółów
NiRmaL
Stracisz $ stateParams, jeśli nie wepchniesz go do adresu URL, lepszym rozwiązaniem jest zapisanie poprzednich parametrów w $ rootScope, możesz je z tego pobrać i wcisnąć do stanu $, aby wrócić.
dangquang1020
Ta odpowiedź jest dla mnie najłatwiejsza do wdrożenia.
Lalnuntluanga Chhakchhuak
8

Stosuję podobne podejście do tego, co robi Endy Tjahjono.

To, co robię, to zapisywanie wartości bieżącego stanu przed dokonaniem przejścia. Zobaczmy na przykładzie; wyobraź sobie to wewnątrz funkcji wykonywanej po kliknięciu na cokolwiek wyzwala przejście:

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

Kluczem jest tutaj obiekt params (mapa parametrów, które zostaną przesłane do stanu) -> { previousState : { name : $state.current.name } }

Uwaga: zwróć uwagę, że ja tylko „zapisuję” atrybut name obiektu $ state, ponieważ jest to jedyna rzecz, której potrzebuję do zapisania stanu. Ale moglibyśmy mieć cały obiekt stanu.

Następnie określ „cokolwiek” zostało zdefiniowane w ten sposób:

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

Tutaj kluczowym punktem jest obiekt params.

params : {
  previousState: null,
}

Następnie w tym stanie możemy uzyskać poprzedni stan w następujący sposób:

$state.params.previousState.name
avcajaraville
źródło
6

Oto naprawdę eleganckie rozwiązanie autorstwa Chrisa Thielena ui-router-extras: $ previousState

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

previousjest obiektem, który wygląda następująco: { state: fromState, params: fromParams }gdzie fromState to poprzedni stan, a fromParams to poprzednie parametry stanu.

Efraim
źródło
1
16 maja 2017 roku Chris Thielen dodał powiadomienie o zakończeniu eksploatacji projektu: ui-router-extras.
Michael R
4

Ok, wiem, że spóźniłem się na imprezę tutaj, ale jestem nowy w Angular. Próbuję dopasować to do przewodnika po stylu Johna Papy . Chciałem, aby to było wielokrotnego użytku, więc utworzyłem w bloku. Oto co wymyśliłem:

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;
        });
    }
}
})();

core.module

(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
}
})();

Każda krytyka dotycząca tego kodu może mi tylko pomóc, więc daj mi znać, gdzie mogę go ulepszyć.

musować
źródło
2

Jeśli potrzebujesz tylko tej funkcji i chcesz jej używać w więcej niż jednym kontrolerze, jest to prosta usługa śledzenia historii tras:

  (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;
  }
})();

gdzie „rdzeń” w .module („rdzeń”) byłby nazwą Twojej aplikacji / modułu. Wymagaj usługi jako zależności od kontrolera, a następnie w kontrolerze możesz wykonać:$scope.routeHistory = RouterTracker.getRouteHistory()

user1970565
źródło
4
Gdybym miał przycisk nawigacyjny na mojej stronie, który przechodzi do poprzedniego stanu w historii, po przejściu do tego poprzedniego stanu zostałby uruchomiony stan stateChangeSuccess, który dodaje go do historii. Czy więc ten kod nie spowodowałby nieskończonej pętli przechodzenia między dwiema stronami?
whatsTheDiff
1

Śledzę poprzednie stany w $ rootScope , więc w razie potrzeby po prostu wywołam poniższą linię kodu.

$state.go($rootScope.previousState);

W App.js :

$rootScope.$on('$stateChangeSuccess', function(event, to, toParams, from, fromParams) {
  $rootScope.previousState = from.name;
});
Naveen Kumar V
źródło
1
Myślę, że lepiej, jeśli oszczędzasz nie tylko z.name. W ten sposób będziesz mieć również parametry na wypadek, gdybyś musiał zrobić coś takiego jak $ state.go ($ tootScope.previousState.name, $ tootScope.previousState.params);
abelabbesnabi
0

W przypadku UI-Router (> = 1.0) zdarzenia StateChange zostały wycofane. Aby uzyskać pełny przewodnik po migracji, kliknij tutaj

Aby uzyskać poprzedni stan bieżącego stanu w UI-Router 1.0+:

app.run(function ($transitions) {
    $transitions.onSuccess({}, function (trans) {
         // previous state and paramaters
         var previousState = trans.from().name;
         var previousStateParameters = trans.params('from');
    });
});
NagarajuRaghava
źródło
-1

Naprawdę prostym rozwiązaniem jest po prostu zmodyfikowanie ciągu $ state.current.name i wycięcie wszystkiego, łącznie z ostatnim znakiem „i po nim”. - otrzymasz nazwę państwa macierzystego. To nie działa, jeśli dużo przeskakujesz między stanami, ponieważ po prostu analizuje bieżącą ścieżkę. Ale jeśli twoje stany odpowiadają temu, gdzie faktycznie jesteś, to działa.

var previousState = $state.current.name.substring(0, $state.current.name.lastIndexOf('.'))
$state.go(previousState)
Juhana Pietari Lehtiniemi
źródło
1
$state.go('^')osiągnie to
James
A co z parametrami stanu?
Tushar Shukla
-2

Możesz zwrócić stan w ten sposób:

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

Przykład:

(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);
        };

    };

})();
otaviodecampos
źródło
2
To nie jest dobra odpowiedź, jeśli uzyskujesz dostęp do stanu z innego stanu. Jeśli chcesz tylko rodzica w obecnym stanie, spełnia to zadanie.
Maxime Lafarie
1
Czy to nie to samo, co użycie $ state.go ("^")?
Nathan Moinvaziri