[javascript] 如何讓後退按鈕與AngularJS UI路由器狀態機配合使用?



Answers

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

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

我已經使用ui-router實現了一個angularjs單頁面應用程序。

最初我使用不同的網址識別每個國家,但是這是針對不友好的,GUID包裝的網址。

所以我現在已經將我的網站定義為一個更簡單的狀態機器。 這些國家不是通過網址來識別的,而是根據需要簡單地轉換,如下所示:

定義嵌套狀態

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 }}

這個系統工作得很好,我喜歡它乾淨的語法。 但是,由於我不使用網址,後退按鈕無法使用。

如何保持我的整潔ui路由器狀態機,但啟用後退按鈕功能?




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



如果你正在尋找最簡單的“後退”按鈕,那麼你可以像這樣設置一個指令:

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

請記住,這是一個相當不聰明的“後退”按鈕,因為它使用瀏覽器的歷史記錄。 如果您將其添加到目標網頁上,則會在用戶登陸之前將其返回給他們的任何網址。




可以使用簡單的指令“回歸歷史”來解決,如果沒有以前的歷史,這一個也是關閉窗口。

指令用法

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

Angular指令聲明

.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-router或不使用。






Related