如何在文件准备好的角度控制器中运行功能?
我的角度控制器中有一个函数,我希望这个函数可以在文档准备好的情况下运行但我注意到角度在创建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
}
谁知道我怎么能这样做?
回答(10)
看这篇文章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
};
这样,您不必等到文档准备就绪 .
Angular在DOMContentLoaded事件时自动初始化,或者在评估angular.js脚本时,如果此时document.readyState设置为'complete' . 此时,Angular会查找指定应用程序根目录的ng-app指令 .
https://docs.angularjs.org/guide/bootstrap
这意味着控制器代码将在DOM准备好后运行 .
因此它只是 $scope.init()
.
Angular有几个时间点来开始执行函数 . 如果你寻求类似jQuery的东西
$(document).ready();
您可能会发现角度模拟非常有用:
$scope.$watch('$viewContentLoaded', function(){
//do something
});
当您想要操作DOM元素时,这个很有用 . 它将在加载所有te元素后开始执行 .
UPD:当您想要更改css属性时,上述内容有效 . 但是,当您想要测量元素属性(例如宽度,高度等)时,有时它不起作用 . 在这种情况下,您可能想尝试这样做:
$scope.$watch('$viewContentLoaded',
function() {
$timeout(function() {
//do something
},0);
});
我有类似的情况,我需要在加载视图后执行控制器功能,并且在视图中的特定第三方组件加载,初始化并在$ 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调用观察者时,oldValue仍将是未定义的 .
这是我在使用coffeescript的外部控制器内部的尝试 . 它工作得很好 . 请注意,settings.screen.xs | sm | md | lg是我在app中包含的非uglified文件中定义的静态值 . 这些值是针对同名媒体查询大小的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()
答案
$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()在大多数情况下是无用的,因为以角度构造的DOM可能还没有准备就绪 .
但更有趣的是,即使在$ viewContentLoaded被触发后,仍然无法找到感兴趣的元素 .
只有在执行$ timeout后才能找到它 . 请注意,即使$ timeout的值为0,在执行之前已经过了将近200毫秒,这表明该线程已经暂停了一段时间,可能是因为DOM在主线程上添加了角度模板 . 从第一个$ document.ready()到最后一个$ timeout执行的总时间接近500毫秒 .
在一个特殊情况下,设置了一个组件的值,然后在$ timeout中更改了text()值,必须增加$ timeout值,直到它工作为止(即使在$ timeout期间可以找到该元素) ) . 第三方组件中的某些异步导致值优先于文本,直到经过足够的时间 . 另一种可能性是$ scope . $ evalAsync,但未尝试过 .
我仍然在寻找那个告诉我DOM完全安定下来并且可以被操纵以便所有案例都能正常工作的事件 . 到目前为止,任意超时值都是必需的,这意味着这是一个可能无法在慢速浏览器上运行的kludge . 我没有尝试像liveQuery和发布/订阅这样可能有效的JQuery选项,但肯定不是纯粹的角度 .
如果你得到像 getElementById call returns null
这样的东西,那么's probably because the function is running, but the ID hasn' t就有时间在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
});
});
}]);
为什么不尝试使用什么角度文档提到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);
...
});
这适用于我 .
2 years ago
我们可以使用
angular.element(document).ready()
方法附加文档准备好时的回调 . 我们可以简单地在控制器中附加回调,如下所示:http://jsfiddle.net/jgentes/stwyvq38/1/