Как мне получить кнопку Назад, чтобы работать с пользовательским интерфейсом маршрутизаторов в AngularJS государственной машины?



я реализовал приложение angularjs одной страницы с помощью ui-router.



Первоначально я идентифицировал каждое состояние с помощью отдельного url-адреса, однако это сделало для недружественных, GUID упакованных URL-адресов.



Так что теперь я определил свой сайт как гораздо более простую государственную машину. Состояния не идентифицируются URL-адресами, а просто переходят по мере необходимости, например:



Определение Вложенных Государства



angular
.module 'app', ['ui.router']
.config ($stateProvider) ->
$stateProvider
.state 'main',
templateUrl: 'main.html'
controller: 'mainCtrl'
params: ['locationId']

.state 'folder',
templateUrl: 'folder.html'
parent: 'main'
controller: 'folderCtrl'
resolve:
folder:(apiService) -> apiService.get '#base/folder/#locationId'


переход в определенное состояние



#The ui-sref attrib transitions to the 'folder' state

a(ui-sref="folder({locationId:'{{folder.Id}}'})")
| {{ folder.Name }}


эта система работает очень хорошо, и я люблю ее чистый синтаксис. Однако, поскольку я не использую URL-адреса, кнопка "назад" не работает.



Как сохранить мой аккуратный ui-маршрутизатор state-machine, но включить функцию кнопки "Назад"?

661   8  

8 ответов:

Примечание

ответы, которые предлагают использовать вариации $window.history.back() все пропустили важную часть вопроса: как восстановить состояние приложения к правильному положению-положению по мере того как история скачет (назад/вперед/освежает). Имея это в виду, пожалуйста, читайте дальше.


да, можно иметь браузер назад / вперед (история) и обновить во время работы чистого ui-router state-machine, но это занимает немного времени.

вам нужно несколько компонентов:

  • Уникальные URL-Адреса. Браузер включает кнопки Назад / Вперед только при изменении URL-адресов, поэтому необходимо создать уникальный url-адрес для каждого посещенного состояния. Однако эти URL-адреса не должны содержать никакой информации о состоянии.

  • Сессия. Каждый сгенерированный url-адрес соотносится с определенным состоянием, поэтому вам нужен способ хранения пар url-состояния, чтобы вы могли извлеките информацию о состоянии после того, как ваше приложение angular было перезапущено щелчками назад / вперед или обновить.

  • История Государства. Простой словарь пользовательский интерфейс-маршрутизатор государства ввел уникальный URL-адрес. Если вы можете положиться на HTML5, то вы можете использовать HTML5 History API, но если, как и я, вы не можете, то вы можете реализовать его самостоятельно в нескольких строках кода (см. ниже).

  • Обслуживания. Наконец, вы должны иметь возможность управлять как изменениями состояния пользовательского интерфейса-маршрутизатора, вызванными внутренним кодом, так и обычными изменениями url-адреса браузера, обычно вызываемыми пользователем, нажимающим кнопки браузера или вводящим материал в панель браузера. Все это может быть немного сложно, потому что легко запутаться в том, что вызвало что.

вот моя реализация каждого из этих требований. Я связал все в три службы:

в Сеанс Обслуживания

class SessionService

    setStorage:(key, value) ->
        json =  if value is undefined then null else JSON.stringify value
        sessionStorage.setItem key, json

    getStorage:(key)->
        JSON.parse sessionStorage.getItem key

    clear: ->
        @setStorage(key, null) for key of sessionStorage

    stateHistory:(value=null) ->
        @accessor 'stateHistory', value

    # other properties goes here

    accessor:(name, value)->
        return @getStorage name unless value?
        @setStorage name, value

angular
.module 'app.Services'
.service 'sessionService', SessionService

это обертка для javascript

app.run(['$window', '$rootScope', 
function ($window ,  $rootScope) {
  $rootScope.goBack = function(){
    $window.history.back();
  }
}]);

<a href="#" ng-click="goBack()">Back</a>

после тестирования различных предложений, я обнаружил, что самый простой способ-лучший.

Если вы используете угловой ui-маршрутизатор и что вам нужна кнопка, чтобы вернуться лучше всего это:

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

или

<a onclick="history.back()>Back</a>

// Предупреждение Не устанавливайте href или путь будет нарушен.

объяснение: Предположим, стандартное приложение управления. Поиск объекта - > просмотр объекта - > редактировать объект

использование угловых решений Из этого состояние :

Поиск -> Вид -> Редактирование

To:

Поиск -> Вид

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

Поиск -> Вид -> Редактирование

и это не логично

однако с помощью простого решения

<a onclick="history.back()"> Back </a>

от :

Поиск -> Вид -> Редактирование

после нажатия на кнопки :

Поиск -> Вид

после нажатия на кнопку браузера назад:

Поиск

последовательность соблюдается. : -)

Если вы ищете самую простую кнопку "назад", то вы можете настроить директиву следующим образом:

    .directive('back', function factory($window) {
      return {
        restrict   : 'E',
        replace    : true,
        transclude : true,
        templateUrl: 'wherever your template is located',
        link: function (scope, element, attrs) {
          scope.navBack = function() {
            $window.history.back();
          };
        }
      };
    });

имейте в виду, что это довольно неразумная кнопка "назад", потому что она использует историю браузера. Если вы включите его на своей целевой странице, он отправит пользователя обратно на любой url, с которого он пришел до посадки на ваш.

решение кнопки Назад/Вперед браузера
Я столкнулся с той же проблемой и решал ее с помощью popstate event из объекта $window и ui-router's $state object. Событие popstate отправляется в окно при каждом изменении активной записи журнала.
Элемент $stateChangeSuccess и $locationChangeSuccess события не запускаются при нажатии кнопки браузера, даже если в адресной строке указано новое местоположение.
Итак, предполагая, что вы перешли из состояния main до folder до main опять же, когда вы нажмете back в браузере, вы должны вернуться к folder маршрут. Путь обновляется, но вид не и по-прежнему отображает все, что у вас есть на main. попробуйте это:

angular
.module 'app', ['ui.router']
.run($state, $window) {

     $window.onpopstate = function(event) {

        var stateName = $state.current.name,
            pathname = $window.location.pathname.split('/')[1],
            routeParams = {};  // i.e.- $state.params

        console.log($state.current.name, pathname); // 'main', 'folder'

        if ($state.current.name.indexOf(pathname) === -1) {
            // Optionally set option.notify to false if you don't want 
            // to retrigger another $stateChangeStart event
            $state.go(
              $state.current.name, 
              routeParams,
              {reload:true, notify: false}
            );
        }
    };
}

кнопки Назад/Вперед должны работать плавно после этого.

Примечание: проверьте совместимость браузера для окна.onpopstate (), чтобы быть уверенным

может быть решена с помощью простой директивы "go-back-history", это также закрывает окно в случае отсутствия предыдущей истории.

использование директивы

<a data-go-back-history>Previous State</a>

Угловое объявления директивы

.directive('goBackHistory', ['$window', function ($window) {
    return {
        restrict: 'A',
        link: function (scope, elm, attrs) {
            elm.on('click', function ($event) {
                $event.stopPropagation();
                if ($window.history.length) {
                    $window.history.back();
                } else {
                    $window.close();  
                }
            });
        }
    };
}])

Примечание: работает с помощью ui-маршрутизатора или нет.

кнопка "Назад" тоже не работала для меня, но я понял, что проблема в том, что у меня было html содержимое внутри моей главной странице, в ui-view элемент.

т. е.

<div ui-view>
     <h1> Hey Kids! </h1>
     <!-- More content -->
</div>

поэтому я переместил содержимое в новый .html файл, и отметил его как шаблон в .js файл с маршрутами.

т. е.

   .state("parent.mystuff", {
        url: "/mystuff",
        controller: 'myStuffCtrl',
        templateUrl: "myStuff.html"
    })

history.back() и перейти к Предыдущее состояние часто дают эффект не то, что вы хотите. Например, если у вас есть форма с вкладками, и каждая вкладка имеет собственное состояние, это просто переключило предыдущую выбранную вкладку, а не возврат из формы. В случае вложенных состояний вам обычно нужно так думать о ведьме родительских состояний, которые вы хотите откатить.

эта директива решает проблемы

angular.module('app', ['ui-router-back'])

<span ui-back='defaultState'> Go back </span>

он возвращается в состояние, которое было активным до отображается. Необязательно defaultState - это имя состояния, которое используется при отсутствии предыдущего состояния в памяти. Также он восстанавливает положение прокрутки

код

class UiBackData {
    fromStateName: string;
    fromParams: any;
    fromStateScroll: number;
}

interface IRootScope1 extends ng.IScope {
    uiBackData: UiBackData;
}

class UiBackDirective implements ng.IDirective {
    uiBackDataSave: UiBackData;

    constructor(private $state: angular.ui.IStateService,
        private $rootScope: IRootScope1,
        private $timeout: ng.ITimeoutService) {
    }

    link: ng.IDirectiveLinkFn = (scope, element, attrs) => {
        this.uiBackDataSave = angular.copy(this.$rootScope.uiBackData);

        function parseStateRef(ref, current) {
            var preparsed = ref.match(/^\s*({[^}]*})\s*$/), parsed;
            if (preparsed) ref = current + '(' + preparsed[1] + ')';
            parsed = ref.replace(/\n/g, " ").match(/^([^(]+?)\s*(\((.*)\))?$/);
            if (!parsed || parsed.length !== 4)
                throw new Error("Invalid state ref '" + ref + "'");
            let paramExpr = parsed[3] || null;
            let copy = angular.copy(scope.$eval(paramExpr));
            return { state: parsed[1], paramExpr: copy };
        }

        element.on('click', (e) => {
            e.preventDefault();

            if (this.uiBackDataSave.fromStateName)
                this.$state.go(this.uiBackDataSave.fromStateName, this.uiBackDataSave.fromParams)
                    .then(state => {
                        // Override ui-router autoscroll 
                        this.$timeout(() => {
                            $(window).scrollTop(this.uiBackDataSave.fromStateScroll);
                        }, 500, false);
                    });
            else {
                var r = parseStateRef((<any>attrs).uiBack, this.$state.current);
                this.$state.go(r.state, r.paramExpr);
            }
        });
    };

    public static factory(): ng.IDirectiveFactory {
        const directive = ($state, $rootScope, $timeout) =>
            new UiBackDirective($state, $rootScope, $timeout);
        directive.$inject = ['$state', '$rootScope', '$timeout'];
        return directive;
    }
}

angular.module('ui-router-back')
    .directive('uiBack', UiBackDirective.factory())
    .run(['$rootScope',
        ($rootScope: IRootScope1) => {

            $rootScope.$on('$stateChangeSuccess',
                (event, toState, toParams, fromState, fromParams) => {
                    if ($rootScope.uiBackData == null)
                        $rootScope.uiBackData = new UiBackData();
                    $rootScope.uiBackData.fromStateName = fromState.name;
                    $rootScope.uiBackData.fromStateScroll = $(window).scrollTop();
                    $rootScope.uiBackData.fromParams = fromParams;
                });
        }]);

Comments

    Ничего не найдено.