发送事件时AngularJS完成加载(Sending event when AngularJS fi

2019-07-20 04:24发布

想知道什么是检测页面加载/引导的光洁度最好的方式,在完成编译/链接的所有指令。

任何事件已经在那里? 我应该重载自举功能?

Answer 1:

只是一种预感:为什么不看ngCloak指令是如何做的? 显然,ngCloak指令管理事情已经加载之后,显示的内容。 我敢打赌,看着ngCloak会导致确切的答案...

编辑1小时后:好了,好了,我看着ngCloak ,它是非常短的。 什么这显然意味着是编译功能将不会执行,直到{{模板}}表达式已被评估(即它加载的模板),因此ngCloak指令的很好的功能。

我的猜测是只做出指示与ngCloak同样简单,那么在你的编译功能做任何你想做的事情。 :)将指令您的应用程序的根元素。 您可以调用指令类似myOnload并把它作为一个属性我-onload事件。 一旦模板已经被编译(表达式评估,并加载子模板)的编译功能将被执行。

编辑23小时后:好了,我做了一些研究,我也问我自己的问题 。 我问的问题是间接与此相关的问题,但它巧合使我认为解决这个问题的答案。

答案是,你可以创建一个简单的指令,并把你的代码指令的链接功能,这(对大多数使用情况,下面解释)时,你的元素准备/加载运行。 基于在该编译和链接功能的执行顺序的Josh的描述 ,

如果你有这样的标记:

 <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指令到应用程序的根元素,并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时要小心。 他们创建自己的范围,您的指令可能不会触发。 例如,如果你把那也有ngIf元素对我们的新ngElementReady指令,以及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$broadcast从在执行的表达或功能的事件ng-element-ready属性。 :)例如:

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

编辑,更几分钟后:

@ satchmorun的回答也工作,但仅限于初始加载。 这里是一个非常有用的,问题描述事物被执行包括链路功能,订单app.run ,等等。 因此,根据你的使用情况, app.run可能是好的,但不是为特定的元素,在这种情况下链接功能更好。

编辑,五个月后,10月17日在8:11 PST:

这不与被异步加载谐音工作。 你将需要添加簿记到您的谐音(例如,一个办法就是让当其内容加载完成,接着发射事件的每个局部跟踪因此父作用域可以算多少泛音已经加载,并且最终做什么它需要做所有的谐音被加载后)。

编辑10月23日下午10时52分PST:

我做了一个简单的指令烧制一些代码,当加载图像:

/*
 * 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 PST:

我提高了我原来的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持续发生占位符已初步被替换后(一$消化可能发生多达10次,直到数据停止改变)。 这将是适用于绝大多数的使用情况。

编辑,10月31日在下午7点26 PST:

好吧,这可能是我最后一次和最后的更新。 这将可能是用例在那里工作99.999:

/*
 * The whenReady directive allows you to execute the content of a when-ready
 * attribute after the element is ready (i.e. when it's done loading all sub directives and DOM
 * content). See: https://stackoverflow.com/questions/14968690/sending-event-when-angular-js-finished-loading
 *
 * Execute multiple expressions in the when-ready attribute by delimiting them
 * with a semi-colon. when-ready="doThis(); doThat()"
 *
 * Optional: If the value of a wait-for-interpolation attribute on the
 * element evaluates to true, then the expressions in when-ready will be
 * evaluated after all text nodes in the element have been interpolated (i.e.
 * {{placeholders}} have been replaced with actual values).
 *
 * Optional: Use a ready-check attribute to write an expression that
 * specifies what condition is true at any given moment in time when the
 * element is ready. The expression will be evaluated repeatedly until the
 * condition is finally true. The expression is executed with
 * requestAnimationFrame so that it fires at a moment when it is least likely
 * to block rendering of the page.
 *
 * If wait-for-interpolation and ready-check are both supplied, then the
 * when-ready expressions will fire after interpolation is done *and* after
 * the ready-check condition evaluates to true.
 *
 * 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;
      var hasReadyCheckExpression = false;

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

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

    if ($attributes.waitForInterpolation && $scope.$eval($attributes.waitForInterpolation)) {
        waitForInterpolation = true;
    }

      if ($attributes.readyCheck) {
        hasReadyCheckExpression = true;
      }

      if (waitForInterpolation || hasReadyCheckExpression) {
        requestAnimationFrame(function checkIfReady() {
          var isInterpolated = false;
          var isReadyCheckTrue = false;

          if (waitForInterpolation && $element.text().indexOf($interpolate.startSymbol()) >= 0) { // if the text still has {{placeholders}}
            isInterpolated = false;
          }
          else {
            isInterpolated = true;
          }

          if (hasReadyCheckExpression && !$scope.$eval($attributes.readyCheck)) { // if the ready check expression returns false
            isReadyCheckTrue = false;
          }
          else {
            isReadyCheckTrue = true;
          }

          if (isInterpolated && isReadyCheckTrue) { evalExpressions(expressions); }
          else { requestAnimationFrame(checkIfReady); }

        });
      }
      else {
        evalExpressions(expressions);
      }
    }
  };
}]);

使用这样的

<div when-ready="isReady()" ready-check="checkIfReady()" wait-for-interpolation="true">
   isReady will fire when this {{placeholder}} has been evaluated
   and when checkIfReady finally returns true. checkIfReady might
   contain code like `$('.some-element').length`.
</div>

当然,它大概可以优化,但我就留在这一点。 requestAnimationFrame是好的。



Answer 2:

在文档的angular.Module ,有描述该条目run功能:

使用此方法来注册时,喷油器加载完成所有模块应进行的工作。

所以,如果你有一些模块,这是你的应用程序:

var app = angular.module('app', [/* module dependencies */]);

该模块已装载后,您可以运行的东西:

app.run(function() {
  // Do post-load initialization stuff here
});

编辑:手动初始化救援

所以,它已经指出,在run时,DOM就绪不会被调用,并联系了起来。 当它被调用$injector通过引用的模块ng-app加载其所有的依赖,这是从DOM编译步骤分开。

我又看看手动初始化 ,并且看起来这应该做的伎俩。

我做了一个小提琴来说明 。

该HTML很简单:

<html>
    <body>
        <test-directive>This is a test</test-directive>
    </body>
</html>

注意缺少的ng-app 。 我有一个指令,会做一些DOM操作,所以我们可以确保秩序和事物时机。

与往常一样,创建一个模块:

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

而这里的指令:

app.directive('testDirective', function() {
    return {
        restrict: 'E',
        template: '<div class="test-directive"><h1><div ng-transclude></div></h1></div>',
        replace: true,
        transclude: true,
        compile: function() {
            console.log("Compiling test-directive");
            return {
                pre: function() { console.log("Prelink"); },
                post: function() { console.log("Postlink"); }
            };
        }
    };
});

我们要更换test-directive与标签div类的test-directive ,并在包裹的内容h1

我添加了返回之前和之后的链接功能,所以我们可以看到,当这些东西运行编译功能。

下面的代码的其余部分:

// The bootstrapping process

var body = document.getElementsByTagName('body')[0];

// Check that our directive hasn't been compiled

function howmany(classname) {
    return document.getElementsByClassName(classname).length;
}

之前,我们已经做了什么,应该有一类的任何元素, test-directive的DOM,我们就大功告成了之后应该有1。

console.log('before (should be 0):', howmany('test-directive'));

angular.element(document).ready(function() {
    // Bootstrap the body, which loades the specified modules
    // and compiled the DOM.
    angular.bootstrap(body, ['app']);

    // Our app is loaded and the DOM is compiled
    console.log('after (should be 1):', howmany('test-directive'));
});

这是非常简单的。 当文件准备好,请致电angular.bootstrap与您的应用程序的根元素和模块名称的数组。

事实上, 如果你附加run功能的app模块 ,你会看到它就会运行任何编译的发生之前。

如果您运行的小提琴和看控制台,您将看到以下内容:

before (should be 0): 0 
Compiling test-directive 
Prelink
Postlink
after (should be 1): 1 <--- success!


Answer 3:

角没有提供一种方法,当一个页面加载完成,也许是因为“已完成”取决于应用程序的信号。 举例来说,如果你有谐音的分层树,一个加载等。 “完成”就意味着所有的人都被加载。 任何框架应该也很难分析你的代码和理解,一切都完成后,或仍有待时。 对于这一点,你就必须提供专用的逻辑来检查和确定。



Answer 4:

我想出了一个解决方案,是在角初始化完成后,评估相对准确。

该指令是:

.directive('initialisation',['$rootScope',function($rootScope) {
            return {
                restrict: 'A',
                link: function($scope) {
                    var to;
                    var listener = $scope.$watch(function() {
                        clearTimeout(to);
                        to = setTimeout(function () {
                            console.log('initialised');
                            listener();
                            $rootScope.$broadcast('initialised');
                        }, 50);
                    });
                }
            };
        }]);

然后可以只是被添加作为对一个属性body元件,然后听使用$scope.$on('initialised', fn)

当没有更多的$消化周期它的工作原理假设应用程序被初始化。 $手表被称为每一个消化周期等启动一个定时器(setTimeout的不是$超时这样一个新的消化周期不触发)。 如果消化周期不超时内发生再假定应用程序已经初始化。

这显然是不作为satchmoruns解决方案,准确的(因为它是可能摘要周期时间长于超时),但我的解决方案并不需要你保持这使得模块的轨道,更容易管理(特别是对于较大的项目)。 总之,似乎是对我的要求不够准确。 希望能帮助到你。



Answer 5:

如果您使用的角度UI路由器 ,你可以监听$viewContentLoaded事件。

“$ viewContentLoaded -燃煤一旦视图被加载,DOM渲染的‘$范围’的观点发出事件之后 。” - 链接

$scope.$on('$viewContentLoaded', 
function(event){ ... });


Answer 6:

我观察的角度DOM操作使用jQuery和我没有设定一个完成我的应用程序(某种预定义和满意的情况下,我需要我的应用程序抽象)的例子,我希望我的NG-中继器的7个结果,并在那里我将设置一个观察功能在使用setInterval的帮助下实现此目的。

$(document).ready(function(){

  var interval = setInterval(function(){

  if($("article").size() == 7){
     myFunction();
     clearInterval(interval);
  }

  },50);

});


Answer 7:

如果你不使用ngRoute模块,即你没有$ viewContentLoaded事件。

您可以使用另一种方法的指令:

    angular.module('someModule')
        .directive('someDirective', someDirective);

    someDirective.$inject = ['$rootScope', '$timeout']; //Inject services

    function someDirective($rootScope, $timeout){
        return {
            restrict: "A",
            priority: Number.MIN_SAFE_INTEGER, //Lowest priority
            link    : function(scope, element, attr){
                $timeout(
                    function(){
                        $rootScope.$emit("Some:event");
                    }
                );
            }
        };
    }

因此,以trusktr的回答它具有最低的优先级。 加上$超时会造成角通过回调执行之前,整个事件循环运行。

$ rootScope使用,因为它允许将指令在应用程序的任何范围和通知仅在必要的听众。

$ rootScope。$发出将火为$ rootScope的事件。$仅听众。 有趣的是,$ rootScope。$广播会通知所有$ rootScope。$上以及$范围。$上监听源



Answer 8:

按照角队,这Github的问题 :

我们现在有$ viewContentLoaded和$了在NG-视图,并发出includeContentLoaded事件分别NG-包括。 我认为这是接近一个可以得到,当我们与编译完成的了解。

在此基础上,似乎这是目前无法以可靠的方式做,否则角度会提供的事件开箱。

自举程序运行意味着在根范围内的消化周期,而且也没有一个消化周期结束事件。

按照角2个设计文档 :

由于多个摘要的,就不可能确定和通知,该模型是稳定的组件。 这是因为通知可以进一步改变数据,可以重新绑定过程。

根据这一点,事实上,这是不可能是一个为什么决定是去在角2重写的原因。



Answer 9:

我有渐渐由通过路由排在了主 - 分装 - 中/后一个片段。

我需要一个subpartial加载后运行的功能,我不想写一个新的指令,并计算出你可以使用一个厚脸皮ngIf

母体部分的控制器:

$scope.subIsLoaded = function() { /*do stuff*/; return true; };

subpartial的HTML

<element ng-if="subIsLoaded()"><!-- more html --></element>


Answer 10:

如果你想与服务器端的数据(JSP,PHP)生成JS则可以将逻辑添加到服务,当加载控制器将被自动加载。

此外,如果你想在所有的指令完成编译/联反应,可以添加在上面的初始化逻辑相应提出了解决方案。

module.factory('YourControllerInitService', function() {

    // add your initialization logic here

    // return empty service, because it will not be used
    return {};
});


module.controller('YourController', function (YourControllerInitService) {
});


Answer 11:

这些都是很好的解决方案,但是,如果您当前使用的路由后来我发现这个解决方案是需要的代码最简单和最少量。 使用“解析”财产等待承诺触发路由之前完成。 例如

$routeProvider
.when("/news", {
    templateUrl: "newsView.html",
    controller: "newsController",
    resolve: {
        message: function(messageService){
            return messageService.getMessage();
    }
}

})

点击这里查看完整的文档-感谢K.斯科特·艾伦



Answer 12:

可能是我可以通过这个例子帮助你

在自定义的fancybox我显示出与插值内容。

在售后服务上,在“开放”的fancybox的方法,我做

open: function(html, $compile) {
        var el = angular.element(html);
     var compiledEl = $compile(el);
        $.fancybox.open(el); 
      }

在$编译整理的数据回报。 您可以检查数据编译



文章来源: Sending event when AngularJS finished loading