为什么$ timeout会影响ui-bootstrap模式中$ stateParams的解析方式?

时间:2014-12-20 03:07:52

标签: javascript angularjs settimeout angular-ui-router angular-ui-bootstrap

ui-router FAQ entry之后,我最近实现了一个通用的AngularJS服务,它将任何ui-router状态定义转换为包含ui-bootstrap模式的状态定义。

作为其中的一部分,需要将resolve对象从状态定义下推到$modal.open()选项。这个问题的主要问题是注入这些解析函数的$stateParams是先前状态的$modal.open()。经过多次努力解决这个问题之后,我发现只需将$timeout调用包裹在$timeout块中就会产生所需的行为。

一般情况下,我想了解为什么这样做的方式,无论是否是可接受的解决方案,以及是否涉及任何警告。在过去,我已经能够通过在$timeout中包含一段代码来解决几个Angular时序问题,这让我感到紧张,因为我真的不确定它为什么会起作用。我猜测在当前的摘要周期结束后$timeout迫使该块运行,但我对此并不太自信。

我创建了一个Plunker来演示这一点 - 如果删除$stateParams并调用模态,则参数将无法解析。

注意:作为一个警告,我必须能够在模态结果中正确地解析$injector.invoke - 我有许多现有的控制器和状态定义,我宁愿不必返回并重构。

后续行动:我已创建ui-router issue 1649以请求解决此问题 - 此外还有一个minimal Plunker仅使用{{1}}来演示无模式的问题所有

2 个答案:

答案 0 :(得分:1)

这种情况正在发生,因为当secondparam正在解决时,您尚未转换到新状态。 $ timeout将您的代码放在队列的末尾,然后发生状态转换,然后执行具有预期状态的代码。您可以通过在解析配置中记录或警告当前状态来判断:

secondparam: ['$stateParams', function($stateParams) {
    alert($state.$current.url); //here
    return $stateParams.secondparam;
}]

不幸的是,onenter和onexit的文档并没有明确说明它们在生命周期中何时被调用:https://github.com/angular-ui/ui-router/wiki#onenter-and-onexit-callbacks。但是this post给出了一些指示:

  

这些回调使我们能够在新视图上触发操作   或者在我们前往另一个州之前。

我认为你最好使用控制器并在那里打开你的模态而不是$ timeout(我相信你的上下文将是window / global)。

$stateProvider.state('demo.modal', {
  url: '/modal/:secondparam',
  template: 'showing modal',
  controller: function($scope, $modal, $state){
    var modalInstance = $modal.open({
      template: '<div class="alert alert-success" role="alert">Remove the $timeout and this will not resolve: {{secondparam}}</div>',
      resolve: {
        secondparam: ['$stateParams', function($stateParams) {
          console.log($state.$current.url, $stateParams); //you are in the new state
          return $stateParams.secondparam; //'secondparam' IS resolved, even without $timeout
        }]
      },
      controller: function($scope, secondparam) {
        $scope.secondparam = secondparam;
      }
    });
    modalInstance.result.finally(function() {
      $state.go('^');
    });
  }
});

请注意,我必须将<div ui-view></div>添加到演示状态才能让控制器实例化(ui-router nested route controller isn't being called)。

$stateProvider.state('demo', {
  url: '/demo/:firstparam',
  template: '<button class="btn btn-primary" ui-sref=".modal({ secondparam: 2 })">Show Modal</button>' +
      '<button class="btn btn-primary" ui-sref="demo.contacts">Show Contacts</button><div ui-view></div>'
});

http://plnkr.co/edit/zdSkAsEyIef0tWxSTC5p?p=preview

答案 1 :(得分:1)

这可能是一种意外的行为,或onEnter的“设计”行为。

解决方案是将$stateParams注入onEnter处理程序 - 它将指向onEnter的第二个状态(根据documentation)的参数。

$stateProvider.state('demo.modal', {
  url: '/modal/:secondparam',
  onEnter: showModal,
  // ...
});

function showModal($state, $stateParams) {
   var secondparam = $stateParams.secondparam; // equals 2
   ...
   // BUT, the state has not yet transitioned
   var secondParamFromState = $state.params.secondparam; // is undefined

   ...
}

$timeout允许状态转换,resolve的{​​{1}}被注入当前参数,这将是第二个状态。

编辑:已更新plunker