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


254

У мене є функція в моєму кутовому контролері, я хотів би, щоб ця функція була запущена на готовому документі, але я помітила, що кутовий запускає його під час створення купола.

 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
 }

Хтось знає, як я можу з цим піти?

Відповіді:


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працює нестандартно.
nshew

17
Для його використання вам потрібно буде ввести $ документ, але це рекомендований спосіб посилання на елемент документа. Вам не доведеться, але це полегшує тестування.
Джейсон Кокс

10
document- це глобальна змінна, де $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
};

Таким чином, Вам не доведеться чекати, поки документ буде готовий.


1
що робити, якщо мені потрібно зателефонувати після завантаження сторінки?
Ріші

22

Кутовий ініціалізується автоматично при DOMContentLoadedподії або коли сценарій angular.js оцінюється, якщо в цей час document.readyState встановлено "завершено". У цей момент Angular шукає директиву ng-app, яка позначає корінь вашої програми.

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

Це означає, що код контролера запуститься після того, як DOM буде готовий.

Таким чином, це просто $scope.init().


9
Я намагався це зробити, але як не дивно, але деякі речі на моїй сторінці не завантажувалися
foreyez

і як знати, коли кутовий код готовий перед виконанням таких дій, як натискання кнопок тощо, під час написання автоматизованих веб-тестів?
akostadinov

Також DOMContentLoadedзапускається, коли документ повністю завантажений та проаналізований, не чекаючи, коли таблиці стилів, зображення та підрамники закінчуються завантаженням. Для отримання додаткової інформації див. У чому різниця між подіями DOMContentLoaded та завантаження? .
georgeawg

22

Кутовий має кілька точок часу для початку виконання функцій. Якщо ви шукаєте щось на зразок jQuery

$(document).ready();

Цей аналог у кутку може бути дуже корисним:

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

Цей варіант корисний, коли потрібно маніпулювати елементами DOM. Він почне виконуватись лише після завантаження всіх елементів.

UPD: сказане вище працює, коли ви хочете змінити властивості css. Однак іноді це не працює, коли потрібно виміряти властивості елементів, наприклад ширину, висоту тощо. У цьому випадку ви можете спробувати це:

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

Це заслуговує більше, ніж запропонована відповідь. Я отримав цю роботу, не використовуючи $ timeout.
Richie86

він працює для мене лише з setTimeout (0) - без нього вміст під ng-повтором, наприклад, ще не завантажується в цей момент
Ігнасіо Вацкес

2

У мене була схожа ситуація, коли мені потрібно було виконати функцію контролера після завантаження представлення даних, а також після того, як певний сторонній компонент у представленні був завантажений, ініціалізований і розмістив посилання на себе в області "$ range". Що для мене працювало - налаштувати годинник на цю властивість області та запустити функцію лише після її ініціалізації.

// $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();
    }
});

Охоронцем називають пару разів із невизначеними значеннями. Тоді, коли сітка створена і має очікувану властивість, функція ініціалізації може бути безпечно викликана. Перший раз, коли спостерігач буде викликаний з невизначеною новою валлю, oldValue все ще буде невизначений.


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()

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

В одному надзвичайному випадку, коли було встановлено значення компонента, а потім значення text () було змінено пізніше в $ timeout, значення $ timeout довелося збільшувати, поки воно не спрацювало (навіть якщо елемент можна було знайти під час $ timeout ). Щось асинхронізація в сторонній компоненті призвела до того, що значення має перевагу над текстом, поки не пройде достатній час. Інша можливість - $ range. $ 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);
        ...
    });

Це працювало для мене.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.