避免在 Angular UI 模式中使用 $timeout

Avoid using $timeout in Angular UI Modal

In this plunk 我有一个包裹在指令中的 Angular UI 模态框。从控制器中,我调用了一个方法来打开模式,但为此我需要使用 $timeout,否则,DOM 还没有完成指令的渲染。

这似乎可行,但是,如果 $timeout 过期后需要完成的事情还没有完成,会发生什么情况? $timeout 可能在开发环境中工作,但在生产环境中可能会失败。使用 $timeout 是一种不好的做法吗?如何避免在此示例中使用它?

HTML

<div modal control="modalCtl"></div>

Javascript

var app = angular.module('app', ['ui.bootstrap']);

app.controller('myCtl', function($scope,$timeout) {
    $scope.modalCtl = {};       
    $timeout(function(){
        $scope.modalCtl.openModal();
    },100);         
})
.directive('modal', function ($uibModal) {
    var directive = {};
    directive.restrict = 'EA';
    directive.scope = {    
        control: '='
    };
    directive.link = function (scope, element, attrs) {
        scope.control = scope.control || {};            
        scope.control.openModal = function() {
            scope.modalInstance = $uibModal.open({
                template: '<button ng-click="close()">Close</button>',
                scope: scope
            })
        };
        scope.close = function () {
            scope.modalInstance.close();
        };
    };
    return directive;
});

使用超时来任意等待代码执行通常是不好的。正如您在问题中所述,根据您正在加载的页面的整体上下文,您无法保证该指令将在您的控制器运行时准备就绪。

你这里的抽象层次似乎太多了。某些东西正在渲染一个 div,当它完全渲染时,会显示一个模态。

只让渲染 div 的东西创建并显示模式不是更有意义吗?

为了避免使用$timeout,指令可以在一切就绪时通知控制器。看看:

.directive('modal', function ($uibModal) {      
    var directive = {};    
    directive.restrict = 'EA';    
    directive.scope = {    
            control: '=',
            onReady: '&'  // <-- bind `onReady` with  `onModalReady`            
        };

    directive.link = function (scope, element, attrs) {

      scope.control = scope.control || {};

      scope.control.openModal = function() {
          scope.modalInstance = $uibModal.open({
              template: '<button ng-click="close()">Close</button>',
              scope: scope
            })                
        };

        scope.close = function () {
            scope.modalInstance.close();
        };

      scope.onReady(); // <-- notify controller
    };    
    return directive;    
});

输出HTML:

 <div modal on-ready="onModalReady()" control="modalCtl"></div>

我们的控制者:

 $scope.onModalReady = function(){
   $scope.modalCtl.openModal();
 }

Changed Plunker


关于评论@Eduardo La Hoz Miranda

you should be ok with the timeout. I would decrease the time to 0, tho, since timeout will send your call to the bottom of the event loop.

通常当我们用 0 毫秒或不带参数初始化 $timeout 时:

 $timeout(function(){
   $scope.modalCtl.openModal();
 }); 

我们在下一个摘要周期a.e之前将$scope.modalCtl.openModal()延迟到运行。排在最后。所以在这种情况下,指令 link 将 运行 从头到尾第一个,只有在您进入 $timeout 之后。

The $timeout may work in a development environment but may fail in production.

在生产环境中,您有相同的代码。它应该工作。我相信问题出在其他地方。如果您对 $timeout 没有信心,请使用我发布的上述方式。

Your Logged Plunker

当指令的 link 功能完成时,它可以发出一条消息,表明它已准备就绪。

并且控制器侦听此消息并在收到时显示模态。

代码:

var app = angular.module('app', ['ui.bootstrap']);

app.controller('myCtl', function($scope,$timeout) {
  $scope.modalCtl = {};
    $scope.$on("hey", function() {
    $scope.modalCtl.openModal();
  });
})
.directive('modal', function ($uibModal) {

  var directive = {};

  directive.restrict = 'EA';

  directive.scope = {    
    control: '='
  };

  directive.link = function (scope, element, attrs) {

  scope.control = scope.control || {};

  scope.control.openModal = function() {
    scope.modalInstance = $uibModal.open({
      template: '<button ng-click="close()">Close</button>',
      scope: scope
  })

  };

  scope.close = function () {
        scope.modalInstance.close();
  };

  scope.$emit("hey");
};

return directive;
});