Як я можу запустити директиву після того, як dom закінчить надання?


115

У мене, здавалося б, проста проблема, без очевидних (читання документів Angular JS) .

У мене є кутова директива JS, яка робить деякі обчислення на основі висоти інших елементів DOM для визначення висоти контейнера в DOM.

Щось подібне до цього відбувається всередині директиви:

return function(scope, element, attrs) {
    $('.main').height( $('.site-header').height() -  $('.site-footer').height() );
}

Проблема полягає в тому, що, коли директива працює, $('site-header') її неможливо знайти, повертаючи порожній масив замість потрібного мені елемента JOMu обернутий DOM.

Чи є зворотний дзвінок, який я можу використовувати в межах своєї директиви, яка працює лише після завантаження DOM, і я можу отримати доступ до інших елементів DOM за допомогою звичайних запитів стилю вибору jQuery?


1
Ви можете використовувати область. $ On () та область. $ Emit () для використання спеціальних подій. Не впевнений, чи це правильний / рекомендований підхід.
Тош

Відповіді:


137

Це залежить від того, як побудовано ваш $ ('site-header').

Ви можете спробувати використовувати $ timeout із затримкою 0. Щось на зразок:

return function(scope, element, attrs) {
    $timeout(function(){
        $('.main').height( $('.site-header').height() -  $('.site-footer').height() );
    });        
}

Пояснення, як це працює: один , два .

Не забудьте ввести $timeoutсвою директиву:

.directive('sticky', function($timeout)

5
Дякую, я намагався змусити це працювати століттями, поки не зрозумів, що не перейшов $timeoutу директиву. До. Все працює зараз, ура.
Jannis

5
Так, вам потрібно перейти $timeoutдо такої директиви:.directive('sticky', function($timeout) { return function (scope, element, attrs, controller) { $timeout(function(){ }); }); };
Володимир Старков

19
Ваші пов'язані пояснення пояснюють, чому фокус тайм-аута працює в JavaScript, але не в контексті AngularJS. З офіційної документації : " [...] 4. Черга $ evalAsync використовується для планування роботи, яка повинна відбуватися за межами поточного кадру стека, але до того, як перегляд браузера відобразиться. Це зазвичай робиться за допомогою setTimeout (0) , але підхід setTimeout (0) страждає від повільності і може спричинити мерехтіння перегляду, оскільки браузер надає перегляд після кожної події. [...] "(акцент мій)
Альберто

12
Я зіткнувся з подібною проблемою і виявив, що мені потрібно близько 300 мс, щоб дозволити DOM завантажуватися перед виконанням моєї директиви. Мені справді не подобається підключати подібні до зовнішніх чисел такі цифри. Я впевнений, що швидкість завантаження DOM буде змінюватися в залежності від користувача. Тож як я можу бути впевненим, що 300 м працюватимуть для тих, хто використовує мій додаток?
holditreal

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

44

Ось як я це роблю:

app.directive('example', function() {

    return function(scope, element, attrs) {
        angular.element(document).ready(function() {
                //MANIPULATE THE DOM
        });
    };

});

1
Не потрібно навіть angular.element, тому що елемент уже доступний:element.ready(function(){
timhc22

1
@ timhc22 Елемент - це посилання на DOMElement, який запустив директиву, Ваша рекомендація не призведе до посилання DOMElement на сторінку об'єкта Document.
тобіус

це не працює належним чином. Я отримую offsetWidth = 0 завдяки такому підходу
Олексій Ш.

37

Можливо, автор більше не потребуватиме моєї відповіді. І все-таки заради повноти я вважаю, що інші користувачі можуть вважати це корисним. Найкраще і найпростіше рішення - використовувати $(window).load()всередині повернутої функції всередині тіла. (або ви можете використовуватиdocument.ready . Це дійсно залежить, потрібні вам всі зображення чи ні).

Використання $timeout мою скромну думку, використання дуже слабкий варіант, а в деяких випадках може бути невдалим.

Ось повний код, який я б використав:

.directive('directiveExample', function(){
   return {
       restrict: 'A',
       link: function($scope, $elem, attrs){

           $(window).load(function() {
               //...JS here...
           });
       }
   }
});

1
Чи можете ви детальніше пояснити, чому це може "не в деяких випадках"? Які випадки ви маєте на увазі?
ритер

6
Ви припускаєте, що jQuery доступний тут.
Джонатан Кремін

3
@JonathanCremin Вибір jQuery - це проблема, що стосується ОП
Нік Деверо

1
Це чудово працює, але якщо є посада, яка створює нові елементи з директивою, тоді завантаження вікна не спрацює після початкового завантаження і, отже, не працюватиме належним чином.
Брайан Скотт

@BrianScott - я використав комбінацію $ (window) .load для початкового візуалізації сторінки (мій варіант використання очікував вбудованих файлів шрифтів), а потім element.ready, щоб подбати про перемикання поглядів.
аааааа

8

є ngcontentloadedподія, я думаю, ви можете її використати

.directive('directiveExample', function(){
   return {
       restrict: 'A',
       link: function(scope, elem, attrs){

                $$window = $ $window


                init = function(){
                    contentHeight = elem.outerHeight()
                    //do the things
                }

                $$window.on('ngcontentloaded',init)

       }
   }
});

21
Чи можете ви пояснити, що $ $windowробить?
Сом

2
схоже на якийсь coffeescript, можливо, малося на увазі це $ ($ window) і $ window вводиться в директиву
mdob

5

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

Додайте $scope.$broadcast("variable_name_here");після того, як потрібний зовнішній ресурс або тривало працює контролер / директива буде виконано.

Потім додайте нижче після завантаження вашого зовнішнього ресурсу.

$scope.$on("variable_name_here", function(){ 
   // DOM manipulation here
   jQuery('selector').height(); 
}

Наприклад, в обіцянці відкладеного запиту HTTP.

MyHttpService.then(function(data){
   $scope.MyHttpReturnedImage = data.image;
   $scope.$broadcast("imageLoaded");
});

$scope.$on("imageLoaded", function(){ 
   jQuery('img').height(80).width(80); 
}

2
Це не вирішить проблему, оскільки завантажені дані не означають, що вони вже надані в DOM, навіть якщо вони мають відповідні змінні області, пов'язані з елементами DOM. Існує часовий проміжок між моментом, коли вони завантажуються в область застосування, і виведеними результатами в dom.
René Stalder

1

У мене була подібна проблема і хочу поділитися тут своїм рішенням.

У мене є такий HTML:

<div data-my-directive>
  <div id='sub' ng-include='includedFile.htm'></div>
</div>

Проблема: У функції посилання директиви батьківського діва я хотів jquery'ing дочірнього div # sub. Але це просто дало мені порожній об'єкт, оскільки ng-include не закінчився, коли функція посилання директиви виконувалась. Тож спершу я зробив брудний спосіб вирішення проблеми з $ timeout, який працював, але параметр затримки залежав від швидкості клієнта (нікому це не подобається).

Працює, але брудно:

app.directive('myDirective', [function () {
    var directive = {};
    directive.link = function (scope, element, attrs) {
        $timeout(function() {
            //very dirty cause of client-depending varying delay time 
            $('#sub').css(/*whatever*/);
        }, 350);
    };
    return directive;
}]);

Ось чисте рішення:

app.directive('myDirective', [function () {
    var directive = {};
    directive.link = function (scope, element, attrs) {
        scope.$on('$includeContentLoaded', function() {
            //just happens in the moment when ng-included finished
            $('#sub').css(/*whatever*/);
        };
    };
    return directive;
}]);

Можливо, це комусь допомагає.

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