【问题标题】:How to run function in AngularJS controller on document ready?如何在文档准备好时在 AngularJS 控制器中运行功能?
【发布时间】:2013-09-09 21:41:03
【问题描述】:

我的 Angular 控制器中有一个函数,我希望在准备好文档时运行该函数,但我注意到 Angular 在创建 dom 时运行它。

 function myController($scope)
 {
     $scope.init = function()
     {
        // I'd like to run this on document ready
     }

     $scope.init(); // doesn't work, loads my init before the page has completely loaded
 }

有人知道我该怎么做吗?

【问题讨论】:

    标签: javascript angularjs onload


    【解决方案1】:

    我们可以使用angular.element(document).ready() 方法在文档准备好时附加回调。我们可以像这样简单地在控制器中附加回调:

    angular.module('MyApp', [])
    
    .controller('MyCtrl', [function() {
        angular.element(document).ready(function () {
            document.getElementById('msg').innerHTML = 'Hello';
        });
    }]);
    

    http://jsfiddle.net/jgentes/stwyvq38/1/

    【讨论】:

    • 或者你可以使用 $document.ready(function(){...}),Angular 文档:docs.angularjs.org/api/ng/service/$document
    • 您需要注入$document 才能使用它。 angular.element 开箱即用。
    • 您需要注入 $document 才能使用它,但这是引用文档元素的推荐方式。您不必这样做,但它使测试变得更容易。
    • document 是一个全局变量,其中$document 可以通过角度注入,从而使测试更容易。一般来说,很难对访问全局 JS 变量(如文档或窗口)的应用程序进行单元测试。我也认为module.run 是放置此代码而不是控制器的好地方。
    • 这对我不起作用,getElementById 调用返回 null。
    【解决方案2】:

    看到这个帖子How to execute angular controller function on page load?
    快速查找:

    // register controller in html
    <div data-ng-controller="myCtrl" data-ng-init="init()"></div>
    
    // in controller
    $scope.init = function () {
        // check if there is query in url
        // and fire search in case its value is not empty
    };
    

    这样,您不必等到文件准备好。

    【讨论】:

    • 如果我需要在页面加载后调用怎么办?
    【解决方案3】:

    Angular 有几个时间点来开始执行函数。如果你在寻找类似 jQuery 的

    $(document).ready();
    

    您可能会发现这个角度模拟非常有用:

    $scope.$watch('$viewContentLoaded', function(){
        //do something
    });
    

    当您想要操作 DOM 元素时,这很有帮助。只有在加载所有 te 元素后才会开始执行。

    UPD:当您想要更改 css 属性时,上述内容有效。但是,有时当您要测量元素属性(例如宽度、高度等)时,它不起作用。在这种情况下,您可能需要尝试以下操作:

    $scope.$watch('$viewContentLoaded', 
        function() { 
            $timeout(function() {
                //do something
            },0);    
    });
    

    【讨论】:

    • 这比建议的答案更值得。我在不使用 $timeout 的情况下完成了这项工作。
    • 它仅适用于 setTimeout (0) - 没有它的情况下,例如 ng-repeat 下的内容此时尚未加载
    【解决方案4】:

    Angular 在DOMContentLoaded 事件或当 如果当时 document.readyState 评估 angular.js 脚本 设置为“完成”。此时 Angular 正在寻找 ng-app 指定您的应用程序根的指令。

    https://docs.angularjs.org/guide/bootstrap

    这意味着控制器代码将在 DOM 准备好后运行。

    因此它只是$scope.init()

    【讨论】:

    • 我尝试这样做,但奇怪的是它不起作用,我的页面中的某些内容没有加载
    • 在编写自动化网络测试时,如何知道角码何时准备就绪,然后再执行单击按钮等操作?
    • 同样DOMContentLoaded在文档完全加载和解析后被触发,无需等待样式表、图像和子框架完成加载。有关详细信息,请参阅What is the difference between DOMContentLoaded and load events?
    【解决方案5】:

    答案

    $scope.$watch('$viewContentLoaded', 
        function() { 
            $timeout(function() {
                //do something
            },0);    
    });
    

    是我测试过的大多数场景中唯一适用的一种。在一个包含 4 个组件的示例页面中,所有这些组件都从模板构建 HTML,事件的顺序是

    $document ready
    $onInit
    $postLink
    (and these 3 were repeated 3 more times in the same order for the other 3 components)
    $viewContentLoaded (repeated 3 more times)
    $timeout execution (repeated 3 more times)
    

    所以 $document.ready() 在大多数情况下是没有用的,因为 Angular 构建的 DOM 可能还远远没有准备好。

    但更有趣的是,即使在 $viewContentLoaded 被触发后,仍然找不到感兴趣的元素。

    只有在 $timeout 执行后才被发现。请注意,尽管 $timeout 的值为 0,但在它执行之前已经过去了将近 200 毫秒,这表明该线程被推迟了很长一段时间,可能是因为 DOM 在主线程上添加了角度模板。从第一次 $document.ready() 到最后一次 $timeout 执行的总时间接近 500 毫秒。

    在一个特殊的情况下,一个组件的值被设置,然后 text() 值在 $timeout 之后被更改,$timeout 值必须增加直到它起作用(即使在$超时)。第 3 方组件中的某些异步导致值优先于文本,直到经过足够的时间。另一种可能是$scope.$evalAsync,但没试过。

    我仍在寻找一个事件,它告诉我 DOM 已经完全稳定下来并且可以进行操作,以便所有情况都有效。到目前为止,一个任意的超时值是必要的,这意味着充其量这是一个可能无法在慢速浏览器上工作的杂物。我没有尝试过诸如 liveQuery 和发布/订阅之类的 JQuery 选项,它们可能会起作用,但肯定不是纯角度的。

    【讨论】:

      【解决方案6】:

      我有类似的情况,我需要在加载视图之后执行控制器函数,并且在加载、初始化视图中的特定 3rd 方组件并在 $scope 上放置对自身的引用之后。最终对我有用的是在这个范围属性上设置一个监视并仅在初始化后触发我的函数。

      // $scope.myGrid property will be created by the grid itself
      // The grid will have a loadedRows property once initialized
      
      $scope.$watch('myGrid', function(newValue, oldValue) {
          if (newValue && newValue.loadedRows && !oldValue) {
              initializeAllTheGridThings();
          }
      });
      

      观察者被调用了几次,带有未定义的值。然后当网格被创建并具有预期的属性时,初始化函数可以被安全地调用。第一次使用未定义的 newValue 调用 watcher 时,oldValue 仍然是未定义的。

      【讨论】:

        【解决方案7】:

        这是我在外部控制器内部使用咖啡脚本的尝试。它工作得很好。请注意,settings.screen.xs|sm|md|lg 是在我包含在应用程序中的非丑化文件中定义的静态值。这些值是针对同名媒体查询大小的 Bootstrap 3 官方断点:

        xs = settings.screen.xs // 480
        sm = settings.screen.sm // 768
        md = settings.screen.md // 992
        lg = settings.screen.lg // 1200
        
        doMediaQuery = () ->
        
            w = angular.element($window).width()
        
            $scope.xs = w < sm
            $scope.sm = w >= sm and w < md
            $scope.md = w >= md and w < lg
            $scope.lg = w >= lg
            $scope.media =  if $scope.xs 
                                "xs" 
                            else if $scope.sm
                                "sm"
                            else if $scope.md 
                                "md"
                            else 
                                "lg"
        
        $document.ready () -> doMediaQuery()
        angular.element($window).bind 'resize', () -> doMediaQuery()
        

        【讨论】:

          【解决方案8】:

          如果您收到类似getElementById call returns null 的信息,可能是因为该函数正在运行,但 ID 还没有来得及加载到 DOM 中。

          尝试延迟使用 Will 的答案(朝向顶部)。示例:

          angular.module('MyApp', [])
          
          .controller('MyCtrl', [function() {
              $scope.sleep = (time) => {
                  return new Promise((resolve) => setTimeout(resolve, time));
              };
              angular.element(document).ready(function () {
                  $scope.sleep(500).then(() => {        
                      //code to run here after the delay
                  });
              });
          }]);
          

          【讨论】:

            【解决方案9】:

            为什么不试试 Angular 文档提到的 https://docs.angularjs.org/api/ng/function/angular.element

            angular.element(回调)

            我在 $onInit(){...} 函数中使用了它。

             var self = this;
            
             angular.element(function () {
                    var target = document.getElementsByClassName('unitSortingModule');
                    target[0].addEventListener("touchstart", self.touchHandler, false);
                    ...
                });
            

            这对我有用。

            【讨论】:

              【解决方案10】:
              $scope.$on('$ViewData', function(event) {
              //Your code.
              });
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2017-04-02
                • 1970-01-01
                • 2014-04-26
                • 1970-01-01
                • 1970-01-01
                • 2013-11-03
                • 2019-04-01
                • 1970-01-01
                相关资源
                最近更新 更多