Angular — пользовательский интерфейс получает предыдущее состояние

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

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

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


person Mihai H    schedule 19.05.2013    source источник
comment
ответ ниже правильный, вы также можете найти всю необходимую информацию из источника, если документов недостаточно goo.gl /9B9bH   -  person David Chase    schedule 07.06.2013


Ответы (14)


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

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

$rootScope.$on('$stateChangeSuccess', function (ev, to, toParams, from, fromParams) {
   //assign the "from" parameter to something
});
person laurelnaiad    schedule 28.05.2013
comment
Какой пример использования from? - person Paul; 19.07.2013
comment
Здесь «в» и «из» означает «в состояние» и «из состояния». Учтите, что ваш предыдущий URL-адрес — localhost: xxxx/employee, а контроллер — «EmployeesController», тогда пример для «fromState»: Object {url: /employees, templateUrl: /employees, controller: EmployeesController, name: employee} - person Ranadheer Reddy; 17.09.2013
comment
Я сделал это в своем реферате: `$rootScope.previousState; $rootScope.currentState; $rootScope.$on('$stateChangeSuccess', function(ev, to, toParams, from, fromParams) { $rootScope.previousState = from.name; $rootScope.currentState = to.name; console.log('Предыдущее состояние: '+$rootScope.previousState) console.log('Текущее состояние:'+$rootScope.currentState) }); ` Он отслеживает предыдущий и текущий в rootScope. Очень удобно! - person Federico; 29.04.2014
comment
Использование решения @endy-tjahjono (stackoverflow.com/a/25945003/2837519) больше соответствует ui-router 1 .Икс. - person Peter Ahlers; 05.01.2017
comment
Мне нравится простой способ с history.back() ‹a href=javascript:history.back() class=btn btn-default no-radius› - person Serip88; 19.10.2017

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

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: angular.copy($state.params),
                        URL: $state.href($state.current.name, $state.params)
                    };
                    return currentStateData;
                }]
            }
        });
}]);
person Endy Tjahjono    schedule 20.09.2014
comment
гораздо лучше, чем иметь дело с $stateChangeSuccess - person SET; 22.12.2014
comment
На мой взгляд, это должен быть принятый ответ. Событие, хотя $stateChangeSuccess работает, оно делает это на глобальном уровне, что на самом деле не требуется большую часть времени. - person Pierre Spring; 16.06.2015
comment
Я согласен. Это намного чище. Если вы имеете дело со многими модулями, все из которых имеют состояния, $stateChangeSuccess будет запускаться при каждом изменении состояния, а не только в вашем модуле. Еще один голос за то, что это лучшее решение. - person Jason Buchanan; 27.10.2015
comment
Это лучше, чем решение $stateChangeSuccess, ЗА ИСКЛЮЧЕНИЕМ случаев, когда вы уже реализовали $stateChangeSuccess по любой другой причине. Затем вы можете получить предыдущее состояние только с одной строкой кода. Проголосуйте за оба ответа :) - person campsjos; 22.12.2015
comment
обратите внимание на случай, когда вы не переходите из другого состояния, но пользователь напрямую переходит к URL-адресу вашего состояния, $state.current будет null - person transang; 09.03.2016
comment
@ Nissassin17, какую версию вы используете? В v0.2.15 при открытии состояния напрямую $state.current является объектом: {name: "", url: "^", views: null, abstract: true}. - person Endy Tjahjono; 09.03.2016
comment
@EndyTjahjono: о, извини. Это была моя ошибка. Я проверил, и он вернул объект, такой же, как ваш {name: "", url: "^", views: null, abstract: true} - person transang; 09.03.2016
comment
@EndyTjahjono: Не могли бы вы уточнить, как мы можем использовать PreviousState.Name в любом другом контроллере? Я попытался ввести его, но получил пустую строку - person A_J; 21.06.2016
comment
К вашему сведению — мне нужно было сделать следующее: Параметры: angular.copy($state.params) — Объяснение: я использую UI-Router v 1.0.0-beta 2, и у меня возникли проблемы с частью Params этого кода; поскольку $state.params является объектом, он будет обновлен до текущего представления после разрешения функции... мне нужно было сделать это в функции разрешения, чтобы предотвратить обновление объекта в текущем представлении. - person Joe H; 29.09.2016
comment
Спасибо за ваше решение. Так как использую версию 1.0.0-beta.3. ваш подход работает. Я подтверждаю, как @JoeH сказал, что вам нужно angular.copy($state.params), потому что js вместо этого устанавливает ссылку на объект, поэтому, когда вы получаете доступ к нему внутри вашего компонента или контроллера, $state будет изменено на ваш текущий объект состояния, и вы потеряете ваши параметры из предыдущего. - person Mikhail.root; 04.12.2016
comment
Этот подход по какой-то причине не поддерживает использование кнопки «Назад». - person Shade; 11.02.2017
comment
Хорошо, но я бы немного изменил это var currentStateData = { Name: $state.current.name, Params: {...$state.params}, URL: $state.href($state.current.name, $state .параметры) }; - person user3597741; 17.02.2020

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

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

$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. Это очень удобно.

person Federico    schedule 29.04.2014
comment
Стоит также сохранить fromParams, если вы действительно хотите перенаправить кого-то туда, откуда он пришел. - person Yaron; 12.02.2015
comment
Мне это нравится, и я использую это в своих приложениях. Спасибо - person Fallenreaper; 04.10.2016
comment
Действительно полезно... Более того, используя localStorage, вы можете получить предыдущее состояние где угодно. - person georgeos; 09.02.2017

В следующем примере я создал 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);
person CodeArtist    schedule 04.02.2016

Добавьте новое свойство под названием {previous} в $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 );
person Luis    schedule 09.06.2016

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

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

OR

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

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

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

person NiRmaL    schedule 27.01.2017
comment
это сложнее, когда у вас есть меню в вашем приложении - person Swanand; 04.05.2017
comment
не понимаю вашу проблему, пожалуйста, предоставьте более подробную информацию - person NiRmaL; 04.05.2017
comment
Вы потеряете $stateParams, если не вставите его в URL-адрес, лучшим решением будет сохранить ваши предыдущие параметры в $rootScope, вы можете получить его оттуда и вставить в $state, чтобы вернуться. - person dangquang1020; 22.09.2017
comment
Этот ответ мне проще всего реализовать. - person Lalnuntluanga Chhakchhuak; 22.06.2018

Я использую тот же подход, что и Энди Тьяджоно.

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

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

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

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

Затем укажите «все», что было определено следующим образом:

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

Здесь ключевым моментом является объект params.

params : {
  previousState: null,
}

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

$state.params.previousState.name
person avcajaraville    schedule 11.06.2015

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

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

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

person Ephraim    schedule 21.10.2015
comment
16 мая 2017 г. Крис Тилен добавил уведомление об окончании срока службы. для проекта: ui-router-extras. - person Michael R; 18.05.2017

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

предыдущийStateProvider

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

ядро.модуль

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

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

person fizch    schedule 13.09.2016

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

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

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

person user1970565    schedule 06.11.2015
comment
Если бы у меня была навигационная кнопка на моей странице, которая переходит в предыдущее состояние в истории, после перехода в это предыдущее состояние будет запущен stateChangeSuccess, который добавит ее в историю. Так не приведет ли этот код к бесконечному циклу перехода между двумя страницами? - person whatsTheDiff; 11.05.2016

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

$state.go($rootScope.previousState);

В App.js:

$rootScope.$on('$stateChangeSuccess', function(event, to, toParams, from, fromParams) {
  $rootScope.previousState = from.name;
});
person Naveen Kumar V    schedule 22.02.2018
comment
Я думаю, будет лучше, если вы сохраните не только from.name. Таким образом, у вас также будут параметры на случай, если вам нужно будет сделать что-то вроде $state.go($tootScope.previousState.name, $tootScope.previousState.params); - person abelabbesnabi; 17.09.2019

Для 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');
    });
});
person NagarajuRaghava    schedule 26.03.2019

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

var previousState = $state.current.name.substring(0, $state.current.name.lastIndexOf('.'))
$state.go(previousState)
person Juhana Pietari Lehtiniemi    schedule 28.05.2016
comment
$state.go('^') выполнит это - person james; 17.06.2016
comment
А как насчет параметров состояния? - person Tushar Shukla; 29.08.2017

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

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

    };

})();
person otaviodecampos    schedule 03.02.2016
comment
Это не очень хороший ответ, если вы обращаетесь к состоянию из неродительского состояния. Если вам просто нужен родитель текущего состояния, он выполняет свою работу. - person Maxime Lafarie; 22.02.2016
comment
Разве это не то же самое, что использовать $state.go(^)? - person Nathan Moinvaziri; 11.10.2016