首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >停止angular-ui-router导航,直到promise被解析

停止angular-ui-router导航,直到promise被解析
EN

Stack Overflow用户
提问于 2013-11-20 19:08:58
回答 10查看 40.1K关注 0票数 46

我想防止当rails设备超时时发生的一些闪烁,但是angular直到资源的下一次授权错误才知道。

发生的情况是,模板被呈现,一些ajax调用资源,然后我们被重定向到rails devise进行登录。我宁愿在每次状态改变时对rails执行ping,如果rails会话已经过期,那么我会在模板呈现之前立即重定向。

ui-router具有可以放在每条路由上的解决方案,但这看起来一点也不干燥。

我有的是这个。但是,只有在状态已经转换之后,才能解析承诺。

代码语言:javascript
复制
$rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams){
        //check that user is logged in
        $http.get('/api/ping').success(function(data){
          if (data.signed_in) {
            $scope.signedIn = true;
          } else {
            window.location.href = '/rails/devise/login_path'
          }
        })

    });

在基于promise的结果呈现新模板之前,我如何中断状态转换?

EN

回答 10

Stack Overflow用户

发布于 2015-10-22 19:33:01

我知道这是非常晚的游戏,但我想抛出我的观点,并讨论我认为是一个很好的方式来“暂停”状态变化。根据angular-ui-router的文档,作为promise的状态的"resolve“对象的任何成员都必须在状态加载完成之前进行解析。所以我的功能性(尽管还没有完善)的解决方案,是在“$stateChangeStart”上的"toState“的resolve对象中添加一个promise:

例如:

代码语言:javascript
复制
$rootScope.$on('$stateChangeStart', function (event, toState, toParams) {
    toState.resolve.promise = [
        '$q',
        function($q) {
            var defer = $q.defer();
            $http.makeSomeAPICallOrWhatever().then(function (resp) {
                if(resp = thisOrThat) {
                    doSomeThingsHere();
                    defer.resolve();
                } else {
                    doOtherThingsHere();
                    defer.resolve();
                }
            });
            return defer.promise;
        }
    ]
});

这将确保状态更改适用于要解决的承诺,这是在API调用完成时完成的,并且做出了基于API返回的所有决策。在允许导航到新页面之前,我已经使用它检查了服务器端的登录状态。当API调用解析时,我要么使用"event.preventDefault()“来停止原始导航,然后路由到登录页面(用if state.name !=”login“括起整个代码块),要么允许用户通过简单地解析延迟承诺来继续,而不是尝试使用绕过布尔值和preventDefault()。

虽然我确信最初的帖子早就解决了他们的问题,但我真的希望这能对其他人有所帮助。

编辑

我想我不想误导别人。如果你不确定你的状态是否有resolve对象,下面是代码应该是什么样子的:

代码语言:javascript
复制
$rootScope.$on('$stateChangeStart', function (event, toState, toParams) {
    if (!toState.resolve) { toState.resolve = {} };
    toState.resolve.pauseStateChange = [
        '$q',
        function($q) {
            var defer = $q.defer();
            $http.makeSomeAPICallOrWhatever().then(function (resp) {
                if(resp = thisOrThat) {
                    doSomeThingsHere();
                    defer.resolve();
                } else {
                    doOtherThingsHere();
                    defer.resolve();
                }
            });
            return defer.promise;
        }
    ]
});

编辑2个

为了使其适用于没有解析定义的州,您需要在app.config中添加以下内容:

代码语言:javascript
复制
   var $delegate = $stateProvider.state;
        $stateProvider.state = function(name, definition) {
            if (!definition.resolve) {
                definition.resolve = {};
            }

            return $delegate.apply(this, arguments);
        };

在stateChangeStart中执行if (!toState.resolve) { toState.resolve = {} };似乎不起作用,我认为ui-router在初始化后不接受resolve判据。

票数 46
EN

Stack Overflow用户

发布于 2013-11-22 03:28:21

我相信你是在找event.preventDefault()

注意:使用event.preventDefault()可以防止转换的发生。

代码语言:javascript
复制
$scope.$on('$stateChangeStart', 
function(event, toState, toParams, fromState, fromParams){ 
        event.preventDefault(); 
        // transitionTo() promise will be rejected with 
        // a 'transition prevented' error
})

尽管我可能会像@charlietfl建议的那样在状态配置中使用resolve

编辑

因此,我有机会在状态更改事件中使用preventDefault(),下面是我所做的:

代码语言:javascript
复制
.run(function($rootScope,$state,$timeout) {

$rootScope.$on('$stateChangeStart',
    function(event, toState, toParams, fromState, fromParams){

        // check if user is set
        if(!$rootScope.u_id && toState.name !== 'signin'){  
            event.preventDefault();

            // if not delayed you will get race conditions as $apply is in progress
            $timeout(function(){
                event.currentScope.$apply(function() {
                    $state.go("signin")
                });
            },300)
        } else {
            // do smth else
        }
    }
)

}

编辑

Newer documentation包含了一个如何在调用preventDefault后使用sync()以继续的示例,但是示例使用了$locationChangeSuccess事件,这对我和评论者来说是不起作用的,而使用下面的示例中的$stateChangeStart,取自带有更新事件的文档:

代码语言:javascript
复制
angular.module('app', ['ui.router'])
    .run(function($rootScope, $urlRouter) {
        $rootScope.$on('$stateChangeStart', function(evt) {
            // Halt state change from even starting
            evt.preventDefault();
            // Perform custom logic
            var meetsRequirement = ...
            // Continue with the update and state transition if logic allows
            if (meetsRequirement) $urlRouter.sync();
        });
    });
票数 27
EN

Stack Overflow用户

发布于 2015-03-03 16:23:10

这是我对这个问题的解决方案。它工作得很好,并且符合这里的一些其他答案的精神。只是稍微清理了一下。我在根作用域上设置了一个名为'stateChangeBypass‘的自定义变量,以防止无限循环。我还在检查状态是否为“login”,如果是,则总是允许的。

代码语言:javascript
复制
function ($rootScope, $state, Auth) {

    $rootScope.$on('$stateChangeStart', function (event, toState, toParams) {

        if($rootScope.stateChangeBypass || toState.name === 'login') {
            $rootScope.stateChangeBypass = false;
            return;
        }

        event.preventDefault();

        Auth.getCurrentUser().then(function(user) {
            if (user) {
                $rootScope.stateChangeBypass = true;
                $state.go(toState, toParams);
            } else {
                $state.go('login');
            }
        });

    });
}
票数 25
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/20094273

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档