Angular - пользовательский интерфейс получает прежнее состояние

152

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

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

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

Михай Х
источник
приведенный ниже ответ является правильным, вы также можете найти всю необходимую информацию из источника, если документации недостаточно, чтобы помочь goo.gl/9B9bH
Дэвид Чейз

Ответы:

133

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

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

$rootScope.$on('$stateChangeSuccess', function (ev, to, toParams, from, fromParams) {
   //assign the "from" parameter to something
});
laurelnaiad
источник
3
Что было бы в качестве примера с использованием "от"?
Пол
Здесь «to» и «from» означает «toState» и «fromState». Предположим, что ваш предыдущий URL-адрес localhost: xxxx / employee, а контроллером - «EmployeesController», тогда пример для «fromState»: Object {url: «employee, templateUrl:« / employee », контроллер:« EmployeesController », name:» сотрудники "}
Ranadheer Reddy
3
Я сделал это в своем резюме: `$ rootScope.previousState; $ rootScope.currentState; $ rootScope. $ on ('$ stateChangeSuccess', функция (ev, to, toParams, from, fromParams) {$ rootScope.previousState = from.name; $ rootScope.currentState = to.name; console.log ('Предыдущее состояние: '+ $ rootScope.previousState) console.log (' Текущее состояние: '+ $ rootScope.currentState)}); `Он отслеживает предыдущие и текущие в rootScope. Довольно удобно!
Федерико
Использование решения @ endy-tjahjono ( stackoverflow.com/a/25945003/2837519 ) является более встроенным в ui-router 1.x.
Питер Алерс
Мне нравится простой способ с history.back () <a href="javascript:history.back()" class="btn btn-default no-radius">
Serip88
146

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

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
гораздо лучше, чем иметь дело$stateChangeSuccess
SET
10
На мой взгляд, это должен быть принятый ответ. Событие, хотя и $stateChangeSuccessработает, оно делает это на глобальном уровне, который на самом деле не требуется большую часть времени.
Пьер Спринг
2
Я согласен. Это намного чище. Если вы имеете дело со многими модулями, у всех из которых есть состояния, $ stateChangeSuccess будет запускаться при каждом изменении состояния, а не только в вашем модуле. Еще один голос за это лучшее решение.
Джейсон Бьюкенен
1
@ Nissassin17, какую версию вы используете? В v0.2.15 при открытии состояния Непосредственно $state.currentявляется объектом: {name: "", url: "^", views: null, abstract: true}.
Энди Тяхджоно
3
К вашему сведению - мне нужно было сделать следующее: Params: angular.copy ($ state.params) - Объяснение: Я использую UI-Router v 1.0.0-beta 2, и у меня возникли проблемы с частью Params этого кода; поскольку $ state.params - это объект, он будет обновляться до текущего представления после разрешения функции ... Мне нужно было сделать это в функции разрешения, чтобы предотвратить обновление объекта в текущем представлении.
Джо Х
100

Для удобства чтения я разместу свое решение (основанное на ответе 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. Это очень удобно.

Federico
источник
19
Стоит также сохранить fromParams, если вы действительно хотите перенаправить кого-то обратно туда, откуда он пришел.
Ярон
Я люблю это и реализую это в своих приложениях. Спасибо
Fallenreaper
Действительно полезно ... Более того, используя localStorage, вы можете получить предыдущее состояние в любом месте.
Георгиос
14

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

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

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

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

Затем в любой controller/ serviceetc вам нужно внедрить $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);
CodeArtist
источник
12

Добавьте новое свойство с именем {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 );
Луис
источник
9

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

//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 ()).

Нирмала
источник
это сложнее, когда у вас есть меню в вашем приложении
Swanand
не
понимаю
Вы потеряете $ stateParams, если вы не введете его в URL-адрес, лучшее решение - сохранить предыдущие параметры в $ rootScope, вы можете получить его и перейти в состояние $, чтобы вернуться назад.
dangquang1020
Этот ответ проще всего реализовать.
Лальнунтлуанга Чхакчуак
8

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

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

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

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

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

Затем укажите «что бы» ни было определено так:

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

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

params : {
  previousState: null,
}

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

$state.params.previousState.name
avcajaraville
источник
6

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

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

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

Ефрем
источник
1
16 мая 2017 года Крис Тилен добавил для проекта Уведомление об окончании жизни : ui-router-extras.
Майкл Р
4

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

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

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

fizch
источник
2

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

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

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

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

$state.go($rootScope.previousState);

В App.js :

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

Для 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');
    });
});
NagarajuRaghava
источник
-1

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

var previousState = $state.current.name.substring(0, $state.current.name.lastIndexOf('.'))
$state.go(previousState)
Юхана Пиетари Лехтиниеми
источник
1
$state.go('^')выполню это
Джеймс
А как насчет государственных параметров?
Тушар Шукла
-2

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

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

    };

})();
otaviodecampos
источник
2
Это плохой ответ, если вы получаете доступ к состоянию из не родительского состояния. Если вы просто хотите родителя текущего состояния, он делает свою работу.
Максим Лафари
1
Разве это не то же самое, что использование $ state.go ("^")?
Натан Моинвазири