javascript 为什么 $state.go 在目标状态或其父级通过 promise 解决时不起作用

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/22050215/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-27 22:17:33  来源:igfitidea点击:

Why $state.go doesn't work when destination state or it's parent resolves with promise

javascriptangularjsangular-ui-router

提问by user3357257

I tried to load some data on parent state with resolve and to redirect user to default state when the app runs like so:

我尝试使用解析加载父状态的一些数据,并在应用程序运行时将用户重定向到默认状态,如下所示:

app.config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) {

  $stateProvider.state('home', {
    url: '/',
    template: '<div><a ui-sref="home">Start App</a> <a ui-sref="home.main">Home</a> <a ui-sref="home.other">Other state</a><div ui-view>Loading...</div></div>',
    resolve: {
      user: ['$timeout', '$q', 
        function($timeout, $q) {
          var deferred = $q.defer();
          var promise = deferred.promise;
          var resolvedVal = promise.then(function() {
            console.log('$timeout executed');
            return {Id: 12, email: '[email protected]', name: 'some name'};
          }, function() {
            console.log('Error happend');
          });
          $timeout(function() {
            deferred.resolve();
          }, 2000);
          return resolvedVal;
        }]
      //user: function() {return {id: 222, name: 'testname', email: '[email protected]'}; }
    },
    controller: ['$scope', 'user', function($scope, user) {
      $scope.user = user;
    }]
  });

  $stateProvider.state('home.other', {
    url: 'other',
    template: '<div>Your name is {{user.name}}, and email is {{user.email}}</div><div>This is other state of application, I try to make it open as default when application starts, by calling to $state.transitionTo() function in app.run() method</div>',
    resolve: {
      someObj: function() {
        console.log('hello');
        return {someProp: 'someValue'};
      }  
    },
    controller: ['$scope', 'user', function($scope, user) {
      $scope.user = user;
    }]    
  });

}]);  

app.run(['$state', '$rootScope', function ($state, $rootScope) {
  $state.go('home.other');   
}]);

And this does not change url in address bar and does not show template of home.other state (though the resolve function of home.state is executed and there is 'hello' in console). But when I comment promise function in resolve and instead put there simple function returning object application redirects as expected.

并且这不会改变地址栏中的url,也不会显示home.other状态的模板(尽管home.state的解析功能被执行并且控制台中有'hello')。但是,当我在 resolve 中注释 promise 函数,而是将返回对象的简单函数放在那里时,应用程序会按预期重定向。

Also instead of $timeout tried to do $http request which will actually be there, but no luck too.

也代替 $timeout 尝试执行 $http 请求,它实际上将在那里,但也没有运气。

回答by user3357257

And to answer my own question - unwanted behavior happend because of digest cycle starts to handle requested url after $state.go('home.other') was called and as the resolve function creates deffered object, though this object is resolved, state engine already passes to the requested url's state. So to prevent this I used the technic explained below:

并回答我自己的问题 - 在 $state.go('home.other') 被调用后,由于摘要循环开始处理请求的 url 并且解析函数创建延迟对象,尽管此对象已解析,状态引擎已经传递到请求的 url 状态。所以为了防止这种情况,我使用了下面解释的技术:

If you need to discard execution of requested url's state resolvation in certain curcumstances when the application startes, you can use $stateChangeStart event, like this:

如果您需要在应用程序启动时放弃在某些情况下执行请求的 url 状态解析,您可以使用 $stateChangeStart 事件,如下所示:

app.run(['$state', '$rootScope', '$timeout', function ($state, $rootScope, $timeout) {
  var appStarted = 0; // flag to redirect only once when app is started
  $rootScope.$on('$stateChangeStart', 
  function(event, toState, toParams, fromState, fromParams) { 
    if(appStarted) return;
    appStarted = 1;   
    event.preventDefault(); //prevents from resolving requested url
    $state.go('home.other'); //redirects to 'home.other' state url
  });  
}]);

回答by Parris Varney

There is also an alternate solution from the same link provided by user3357257which I think is a little bit cleaner.

user3357257 提供同一链接中还有一个替代解决方案,我认为它更简洁一些。

app.run(['$state', '$rootScope', '$timeout', function ($state, $rootScope, $timeout) {
  $timeout(function() { $state.go('home.other'); });   
}]);

The trick is to wrap $state.go()into $timeout()so that it doesn't get overridden.

诀窍是包装$state.go()进去,$timeout()这样它就不会被覆盖。