Angular - ui-router получает предыдущее состояние

Есть ли способ получить предыдущее состояние текущего состояния?

Например, я хотел бы знать, что было в предыдущем состоянии до текущего состояния B (где предыдущим состоянием было бы состояние A).

Я не могу найти его на страницах ui-router github doc.

Ответ 1

ui-router не отслеживает предыдущее состояние после его перехода, но событие $stateChangeSuccess транслируется на $rootScope при изменении состояния.

Вы можете уловить предыдущее состояние из этого события (from - это состояние, которое вы покидаете):

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

Ответ 2

Я использую разрешение для сохранения текущих данных состояния перед переходом в новое состояние:

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

Ответ 3

Для удобства чтения я разместил свое решение (основанное на stu.salsbury anwser) здесь.

Добавьте этот код в шаблон абстрактного приложения, чтобы он работал на каждой странице.

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

Отслеживает изменения в rootScope. Его очень удобно.

Ответ 4

В следующем примере я создал decorator (выполняется только один раз для каждого приложения на этапе конфигурации) и добавляет дополнительное свойство в службу $state, поэтому этот подход не добавляет глобальные переменные в $rootscope и не требует для добавления дополнительной зависимости к другим службам, чем $state.

В моем примере мне нужно было перенаправить пользователя на индексную страницу, когда он уже был подписан, и когда он не перенаправлял его на предыдущую "защищенную" страницу после входа.

Единственными неизвестными услугами (для вас), которые я использую, являются authenticationFactory и appSettings:

  • authenticationFactory просто управляет логином пользователя. В этом случае я использую только метод для определения того, зарегистрирован ли пользователь или нет.
  • appSettings - константы, для которых не используются строки везде. appSettings.states.login и appSettings.states.register содержат имя состояния для имени входа и регистрации.

Затем в любом controller/service и т.д. вам необходимо ввести $state службу, и вы можете получить доступ к текущему и предыдущему URL-адресам следующим образом:

  • Ток: $state.current.name
  • Предыдущая: $state.previous.route.name

На консоли Chrome:

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

Реализация:

(Я использую angular-ui-router v0.2.17 и 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);

Ответ 5

Добавить новое свойство, называемое {предыдущим}, в состояние $state на $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;
    ...
}

Теперь, когда вам нужно, вы можете использовать $state, у вас будет предыдущий доступный

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

И используйте его так:

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

Ответ 6

Я застрял с той же проблемой и нашел самый простой способ сделать это...

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

ИЛИ

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

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

(Если вы хотите, чтобы это было более проверяемым, добавьте службу $window в свой контроллер и используйте $window.history.back()).

Ответ 7

Я использую аналогичный подход к тому, что делает Энди Тхаджоно.

Я делаю это, чтобы сохранить значение текущего состояния перед выполнением перехода. Давайте посмотрим на примере; представьте это внутри функции, выполняемой при нажатии на любой триггер перехода:

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

Ключевым здесь является объект params (карта параметров, которые будут отправлены в состояние) → { previousState : { name : $state.current.name } }

Примечание: обратите внимание, что Im только "сохраняет" атрибут name объекта $state, потому что это единственное, что мне нужно для сохранения состояния. Но мы могли бы иметь весь объект состояния.

Затем, состояние "все равно" определяется следующим образом:

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

Здесь ключевой момент - это объект params.

params : {
  previousState: null,
}

Затем, внутри этого состояния, мы можем получить предыдущее состояние следующим образом:

$state.params.previousState.name

Ответ 8

Вот действительно элегантное решение от Chris Thielen ui-router-extras: $previousState

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

previous - это объект, который выглядит так: { state: fromState, params: fromParams }, где fromState - это предыдущее состояние, а fromParams -  предыдущие параметры состояния.

Ответ 9

Хорошо, я знаю, что я опаздываю на вечеринку здесь, но я новичок в angular. Я стараюсь сделать это в руководство по стилю John Papa здесь. Я хотел сделать это многоразовым, поэтому я создал его в блоке. Вот что я придумал:

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

Любая критика в этом коде поможет мне, поэтому, пожалуйста, дайте мне знать, где я могу улучшить этот код.

Ответ 10

Если вам просто нужна эта функциональность и вы хотите использовать ее в нескольких контроллерах, это простой сервис для отслеживания истории маршрута:

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

где "core" в .module( "core" ) будет именем вашего приложения/модуля. Требовать сервис как зависимость от вашего контроллера, а затем в вашем контроллере вы можете сделать: $scope.routeHistory = RouterTracker.getRouteHistory()

Ответ 11

Я отслеживаю предыдущие состояния в $ rootScope, поэтому всякий раз, когда потребуется, я просто вызываю следующую строку кода.

$state.go($rootScope.previousState);

В App.js:

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

Ответ 12

Для UI-Router (> = 1.0) события StateChange устарели. Чтобы получить полное руководство по миграции, нажмите здесь

Чтобы получить предыдущее состояние текущего состояния в 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');
    });
});

Ответ 13

Очень простое решение - просто отредактировать строку $state.current.name и вырезать все, включая и после последнего '.' - вы получите имя родительского состояния. Это не работает, если вы много прыгаете между состояниями, потому что он просто анализирует текущий путь. Но если ваши состояния соответствуют тому, где вы на самом деле, тогда это работает.

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

Ответ 14

Вы можете вернуть состояние следующим образом:

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

Пример:

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

    };

})();