Как запустить функцию в контроллере AngularJS на готовом документе?

254

У меня есть функция внутри моего углового контроллера, я хотел бы, чтобы эта функция запускалась на готовом документе, но я заметил, что 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
 }

Кто-нибудь знает, как я могу пойти по этому поводу?

foreyez
источник

Ответы:

449

Мы можем использовать 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/

Будет
источник
64
или вы можете использовать $ document.ready (function () {...}), Angular Docs: docs.angularjs.org/api/ng/service/$document
StuR
29
Вы должны будете ввести, $documentчтобы использовать это. angular.elementработает из коробки.
ноября
17
Вам нужно будет ввести $ document, чтобы использовать его, но это рекомендуемый способ ссылки на элемент документа. Вам не нужно, но это облегчает тестирование.
Джейсон Кокс
10
documentявляется глобальной переменной, где as $documentвводится под углом и, таким образом, облегчает тестирование. В общем, сложно тестировать приложения, которые обращаются к глобальным переменным JS, таким как документ или окно. Я также думаю, что module.runэто хорошее место, чтобы поместить этот код вместо контроллера.
lanoxx
7
Это не работает для меня, вызов getElementById возвращает ноль.
ThePartyTurtle
29

Смотрите этот пост Как выполнить функцию углового контроллера при загрузке страницы?
Для быстрого поиска:

// 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
};

Таким образом, вам не нужно ждать, пока документ будет готов.

vinesh
источник
1
Что делать, если мне нужно позвонить после загрузки страницы?
Риши
22

Angular инициализируется автоматически по DOMContentLoadedсобытию или при оценке сценария angular.js, если в это время для document.readyState установлено значение «complete». На данный момент Angular ищет директиву ng-app, которая обозначает корень вашего приложения.

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

Это означает, что код контроллера будет запущен после того, как DOM будет готов.

Таким образом, это просто $scope.init().

Будет М
источник
9
Я пытался сделать это, но, как ни странно, это не сработало, некоторые вещи на моей странице не были загружены
foreyez
и как узнать, готов ли угловой код, прежде чем выполнять такие действия, как нажатие кнопок и т. д. при написании автоматизированных веб-тестов?
Акостадинов
Также DOMContentLoadedзапускается, когда документ полностью загружен и проанализирован, не дожидаясь окончания загрузки таблиц стилей, изображений и подкадров. Для получения дополнительной информации см. В чем разница между DOMContentLoaded и событиями загрузки? ,
georgeawg
22

Angular имеет несколько временных точек для начала выполнения функций. Если вы ищете что-то вроде jQuery's

$(document).ready();

Вы можете найти этот аналог в угловой очень полезным:

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

Это полезно, когда вы хотите манипулировать элементами DOM. Он начнет выполняться только после загрузки всех элементов te.

UPD: сказанное выше работает, когда вы хотите изменить свойства CSS. Однако иногда это не работает, когда вы хотите измерить свойства элемента, такие как ширина, высота и т. Д. В этом случае вы можете попробовать это:

$scope.$watch('$viewContentLoaded', 
    function() { 
        $timeout(function() {
            //do something
        },0);    
});
Алекс Линк
источник
Это заслуживает большего, чем предлагаемый ответ. Я получил эту работу без использования $ timeout.
Richie86
у меня это работает только с setTimeout (0) - без него содержимое под ng-repeat например еще не загружено на данный момент
Ignacio Vazquez
2

У меня была похожая ситуация, когда мне нужно было выполнить функцию контроллера после загрузки представления, а также после загрузки, инициализации определенного стороннего компонента в представлении и размещения ссылки на себя в $ 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 по-прежнему будет неопределенным.

MikeyM
источник
1

Вот моя попытка внутри внешнего контроллера с использованием coffeescript. Это работает довольно хорошо. Обратите внимание, что 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()
akutz
источник
1

Ответ

$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 было равно 0, до его выполнения прошло около 200 миллисекунд, что указывает на то, что этот поток удерживался довольно долго, предположительно, в то время как в DOM были добавлены угловые шаблоны в основной поток. Общее время от первого выполнения $ document.ready () до последнего выполнения $ timeout составило почти 500 миллисекунд.

В одном необычном случае, когда было установлено значение компонента, а затем значение text () было изменено позже в $ timeout, значение $ timeout нужно было увеличивать до тех пор, пока оно не заработало (даже если элемент можно было найти во время $ timeout ). Что-то асинхронное в стороннем компоненте заставило значение иметь приоритет над текстом, пока не прошло достаточно времени. Другая возможность - это $ scope. $ EvalAsync, но она не была опробована.

Я все еще ищу одно событие, которое говорит мне, что DOM полностью остановился и им можно манипулировать, чтобы все случаи работали. Пока что необходимо произвольное значение тайм-аута, то есть в лучшем случае это кладжа, которая может не работать в медленном браузере. Я не пробовал варианты JQuery, такие как liveQuery и публикация / подписка, которые могут работать, но, конечно, не являются чисто угловатыми.

Рэй Смоллвуд
источник
1

Если вы получаете что-то вроде этого getElementById call returns null, это, вероятно, потому, что функция работает, но идентификатор не успел загрузить в DOM.

Попробуйте использовать ответ Уилла (к началу) с задержкой. Пример:

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
        });
    });
}]);
Мистер Грин
источник
0

Почему бы не попробовать с какими угловыми документами упомянуть 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);
        ...
    });

Это сработало для меня.

Mahib
источник
0
$scope.$on('$ViewData', function(event) {
//Your code.
});
Шреянш Кумар
источник