AngularJS加载完成后发送事件

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AngularJS加载完成后发送事件相关的知识,希望对你有一定的参考价值。

当所有指令完成编译/链接时,想知道检测页面加载/引导完成的最佳方法是什么。

有没有活动?我应该重载引导功能吗?

答案

只是预感:为什么不看看ngCloak指令是如何做到的呢?显然,ngCloak指令设法在事物加载后显示内容。我打赌看看ngCloak会得出确切的答案......

编辑1小时后:好吧,好吧,我看了ngCloak,它真的很短。这显然意味着编译函数在评估{{template}}表达式(即它加载的模板)之后才会执行,因此ngCloak指令的功能很好。

我的有根据的猜测是只使用与ngCloak相同的简单性来制作一个指令,然后在你的编译函数中做你想做的任何事情。 :)将指令放在应用程序的根元素上。您可以调用类似myOnload的指令,并将其用作my-onload属性。编译模板(编译表达式并加载子模板)后,将执行编译功能。

编辑,23小时后:好的,所以我做了一些研究,我也是asked my own question。我问的问题与这个问题间接相关,但它巧合地引出了解决这个问题的答案。

答案是你可以创建一个简单的指令,并将你的代码放在指令的链接函数中,当你的元素准备好/加载时,它将运行(对于大多数用例,如下所述)。基于Josh's description of the order in which compile and link functions are executed

如果你有这个标记:

<div directive1>
  <div directive2>
    <!-- ... -->
  </div>
</div>

然后AngularJS将通过以特定顺序运行指令函数来创建指令:

directive1: compile
  directive2: compile
directive1: controller
directive1: pre-link
  directive2: controller
  directive2: pre-link
  directive2: post-link
directive1: post-link

默认情况下,直接“链接”函数是一个后链接,因此外部指令1的链接函数在内部指令2的链接函数运行之后才会运行。这就是为什么我们说在后链接中进行DOM操作是唯一安全的。因此,对于原始问题,从外部指令的链接函数访问子指令的内部html应该没有问题,尽管必须编译动态插入的内容,如上所述。

由此我们可以得出结论,当一切准备就绪/编译/链接/加载时,我们可以简单地制定一个指令来执行我们的代码:

    app.directive('ngElementReady', [function() {
        return {
            priority: -1000, // a low number so this directive loads after all other directives have loaded. 
            restrict: "A", // attribute only
            link: function($scope, $element, $attributes) {
                console.log(" -- Element ready!");
                // do what you want here.
            }
        };
    }]);

现在你可以做的是将ngElementReady指令放到app的根元素上,console.log会在加载时触发:

<body data-ng-app="MyApp" data-ng-element-ready="">
   ...
   ...
</body>

就这么简单!只需制作一个简单的指令并使用它。 ;)

您可以进一步自定义它,以便通过向其添加$scope.$eval($attributes.ngElementReady);来执行表达式(即函数):

    app.directive('ngElementReady', [function() {
        return {
            priority: Number.MIN_SAFE_INTEGER, // execute last, after all other directives if any.
            restrict: "A",
            link: function($scope, $element, $attributes) {
                $scope.$eval($attributes.ngElementReady); // execute the expression in the attribute.
            }
        };
    }]);

然后你可以在任何元素上使用它:

<body data-ng-app="MyApp" data-ng-controller="BodyCtrl" data-ng-element-ready="bodyIsReady()">
    ...
    <div data-ng-element-ready="divIsReady()">...<div>
</body>

只需确保您的元素所在的范围(在控制器中)中定义了您的函数(例如bodyIsReady和divIsReady)。

警告:我说这适用于大多数情况。使用某些指令(如ngRepeat和ngIf)时要小心。他们创建自己的范围,您的指令可能不会触发。例如,如果您将新的ngElementReady指令放在也具有ngIf的元素上,并且ngIf的条件求值为false,那么我们的ngElementReady指令将不会被加载。或者,例如,如果将新的ngElementReady指令放在也具有ngInclude指令的元素上,则如果ngInclude的模板不存在,则不会加载我们的指令。您可以通过确保嵌套指令而不是将它们全部放在同一元素上来解决其中一些问题。例如,通过这样做:

<div data-ng-element-ready="divIsReady()">
    <div data-ng-include="non-existent-template.html"></div>
<div>

而不是这个:

<div data-ng-element-ready="divIsReady()" data-ng-include="non-existent-template.html"></div>

ngElementReady指令将在后一个示例中编译,但它的链接函数将不会被执行。注意:指令总是被编译,但是它们的链接函数并不总是执行,具体取决于上面的某些情况。

编辑,几分钟后:

哦,为了完全回答这个问题,你现在可以从$emit属性中执行的表达式或函数$broadcastng-element-ready你的事件。 :)例如:

<div data-ng-element-ready="$emit('someEvent')">
    ...
<div>

编辑,甚至几分钟后:

@satchmorun的答案也适用,但仅适用于初始加载。这是一个描述事物执行顺序的very useful SO question,包括链接函数,app.run等。因此,根据您的使用情况,app.run可能是好的,但不适用于特定元素,在这种情况下链接功能更好。

编辑,五个月后,10月17日太平洋标准时间8:11:

这不适用于异步加载的部分。您需要在部分中添加簿记(例如,一种方法是使每个部分跟踪其内容何时完成加载然后发出事件,以便父作用域可以计算已加载了多少部分并最终执行它需要的内容加载完所有部分后再做)。

编辑,10月23日太平洋标准时间晚上10:52:

我做了一个简单的指令,用于在加载图像时触发一些代码:

/*
 * This img directive makes it so that if you put a loaded="" attribute on any
 * img element in your app, the expression of that attribute will be evaluated
 * after the images has finished loading. Use this to, for example, remove
 * loading animations after images have finished loading.
 */
  app.directive('img', function() {
    return {
      restrict: 'E',
      link: function($scope, $element, $attributes) {
        $element.bind('load', function() {
          if ($attributes.loaded) {
            $scope.$eval($attributes.loaded);
          }
        });
      }
    };
  });

编辑,10月24日上午12:48太平洋标准时间:

我改进了我原来的ngElementReady指令并将其重命名为whenReady

/*
 * The whenReady directive allows you to execute the content of a when-ready
 * attribute after the element is ready (i.e. done loading all sub directives and DOM
 * content except for things that load asynchronously like partials and images).
 *
 * Execute multiple expressions by delimiting them with a semi-colon. If there
 * is more than one expression, and the last expression evaluates to true, then
 * all expressions prior will be evaluated after all text nodes in the element
 * have been interpolated (i.e. {{placeholders}} replaced with actual values). 
 *
 * Caveats: if other directives exists on the same element as this directive
 * and destroy the element thus preventing other directives from loading, using
 * this directive won't work. The optimal way to use this is to put this
 * directive on an outer element.
 */
app.directive('whenReady', ['$interpolate', function($interpolate) {
  return {
    restrict: 'A',
    priority: Number.MIN_SAFE_INTEGER, // execute last, after all other directives if any.
    link: function($scope, $element, $attributes) {
      var expressions = $attributes.whenReady.split(';');
      var waitForInterpolation = false;

      function evalExpressions(expressions) {
        expressions.forEach(function(expression) {
          $scope.$eval(expression);
        });
      }

      if ($attributes.whenReady.trim().length == 0) { return; }

      if (expressions.length > 1) {
        if ($scope.$eval(expressions.pop())) {
          waitForInterpolation = true;
        }
      }

      if (waitForInterpolation) {
        requestAnimationFrame(function checkIfInterpolated() {
          if ($element.text().indexOf($interpolate.startSymbol()) >= 0) { // if the text still has {{placeholders}}
            requestAnimationFrame(checkIfInterpolated);
          }
          else {
            evalExpressions(expressions);
          }
        });
      }
      else {
        evalExpressions(expressions);
      }
    }
  }
}]);

例如,在加载元素并且someFunction尚未替换时,像这样使用它来触发{{placeholders}}

<div when-ready="someFunction()">
  <span ng-repeat="item in items">{{item.property}}</span>
</div>

在所有someFunction占位符被替换之前,将召集item.property

根据需要评估尽可能多的表达式,并使最后一个表达式true等待{{placeholders}}进行如下评估:

<div when-ready="someFunction(); anotherFunction(); true">
  <span ng-repeat="item in items">{{item.property}}</span>
</div>

someFunctionanotherFunction将在{{placeholders}}被替换后解雇。

这仅在第一次加载元素时起作用,而不是在将来的更改中起作用。如果$digest在占位符最初被替换后继续发生($ d

以上是关于AngularJS加载完成后发送事件的主要内容,如果未能解决你的问题,请参考以下文章

带有服务器发送事件的 AngularJS

AngularJS谷歌地图点击事件不在ios设备ipad和iphone中触发

angularjs加载渲染完页面怎么更改数据?

vue3在加载页面时发送请求

由于事件导致模型更改后AngularJS不刷新视图

如何使用事件侦听器来加载动画片段的循环