Событие AngularJs для вызова после загрузки контента


164

У меня есть функция, которую я хочу вызвать после загрузки содержимого страницы. Я прочитал о $ viewContentLoaded, и он не работает для меня. Я ищу что-то вроде

document.addEventListener('DOMContentLoaded', function () { 
     //Content goes here 
}, false);

Вышеупомянутый вызов не работает для меня в контроллере AngularJs.


1
Это хорошо работает stackoverflow.com/q/14968690/6521116
— Крис Руф

Ответы:


164

Согласно документации $ viewContentLoaded , он должен работать

Испускается каждый раз, когда содержимое ngView перезагружается.

$viewContentLoaded событие генерируется, что означает, что для получения этого события вам нужен родительский контроллер, такой как

<div ng-controller="MainCtrl">
  <div ng-view></div>
</div>

От MainCtrlвас можно прослушать событие

  $scope.$on('$viewContentLoaded', function(){
    //Here your view content is fully loaded !!
  });

Проверьте демо


59
Проблема с этим подходом состоит в том, что на этом этапе контроллеры не полностью инициализированы.
— Эш Блю

12
@Reza Он прав, это событие вызывается при добавлении dom, но до того, как angular завершил обработку dom. Вы можете проверить это, добавив id или класс в качестве угловой переменной и попытаться найти его в $ viewContentLoaded с помощью jQuery. Вы не найдете это.
— Томас Кекейсен

1
@Reza да, Томас прав, элемент формы не может получить доступ внутри, скажем, $ viewContentLoaded, $ scope.formName.elementName. $ SetValidity ("validateRule", false); будет выдавать «Ошибка типа: невозможно прочитать свойство 'elementName' из неопределенного"
— Mussammil

2
То же самое можно вызвать на уровне $ rootScope. Вместо $ scope. $ On это должен быть $ rootScope. $ On и внутри блока app.run ()
— Виль

4
Эта опция не будет работать, если у вас есть ng-repeatи несколько вложенных директив, которые будут генерировать HTML / контент на основе сложных циклов и условий. Рендеринг продолжается некоторое время, даже после $viewContentLoadedзапуска события. Как вы можете убедиться, что все элементы были полностью отрисованы, а затем выполнить определенный код? Есть отзывы?
— tarekahf

104

Угловой <1.6.X

angular.element(document).ready(function () {
    console.log('page loading completed');
});

Угловой> = 1.6.X

angular.element(function () {
    console.log('page loading completed');
});

Это входит в app.js?
— zcoon

3
вы можете послушать его в вашем controllers.js
— hariszaman

2
Работает нормально в основном файле app.js.
— Лубот

Этот ответ работает для меня! Просто посмотрите, что на странице элемента документации 'ready () (устарело, используйте angular.element (callback) вместо angular.element (document) .ready (callback))'
— Lovera

Это сработало для меня. Подход принятого ответа не работает внутри контроллера.
— Фабиано

76

фиксированный - 2015.06.09

Используйте директиву и метод углового элемента readyследующим образом:

JS

.directive( 'elemReady', function( $parse ) {
   return {
       restrict: 'A',
       link: function( $scope, elem, attrs ) {    
          elem.ready(function(){
            $scope.$apply(function(){
                var func = $parse(attrs.elemReady);
                func($scope);
            })
          })
       }
    }
})

HTML

<div elem-ready="someMethod()"></div>

или для тех, кто использует контроллер-как синтаксис ...

<div elem-ready="vm.someMethod()"></div>

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

Возможно, вам придется расставить приоритеты в этой директиве, если у вас есть другие директивы, работающие на том же узле.


это похоже на действительно хорошее решение, но я не мог заставить его работать. Он задохнулся от elem.ready( $scope.$apply( readyFunc( $scope ))); любых идей, почему это может быть? Возможно, обновление для angular произошло? Во всяком случае - это элегантный подход, если бы он мог работать.
— Домоаригато

1
Я вижу, в чем проблема. Была опечатка attrs.onReady, должно быть то, что есть сейчас. Другая проблема заключалась в том, что я назвал это забавным .... ... что я получаю за преобразование coffeescript из памяти в JS.
— Юзопи

1
Я думаю, что в некоторых случаях это может быть хорошо, но мои аргументы против этого таковы: я думаю, что ожидания его кода при простом взгляде состоят в том, что он вернет какое-то текстовое значение для отображения в DOM, поэтому для этого требуется внимательный взгляд; Это не допускает никакой синхронизации с точки зрения приоритета директивы, вы застряли на уровне контроллера, если не используете $timeout; Вы явно создаете надстройку. DOM-узел просто для выполнения не-DOM-логики; Его можно использовать повторно, только если этот метод находится далеко в иерархии $ scope, так что дочерние области наследуют его, я просто думаю, что он выглядит плохо в перспективе NG;
— Юзопи

5
Работал для меня после добавления $timeout(function() {})всего elem.ready(). В противном случае это была $digest already in progressошибка. Но в любом случае, спасибо огромное! Я боролся с этим в течение последнего часа.
— rkrishnan

1
Копание $ scope кажется пустым. Любые идеи?
— MiloTheGreat

66

Вы можете напрямую позвонить, добавив {{YourFunction()}} после HTML-элемента.

Вот такой Plunker Link.


6
Не могли бы вы более подробно изложить свой ответ, добавив чуть больше описания предлагаемого вами решения?
— abarisone

Я думаю, что вы хотите вызвать функцию, когда этот HTML-элемент загружен. Так что вызовите эту функцию, как указано выше. Если мои предположения верны в отношении ваших сомнений, тогда это сработает, в противном случае, пожалуйста, уточните свой вопрос. :)
— синий

14
При вызове такой функции Убедитесь, что он вызывается один раз или иначе цикл дайджеста будет бесконечным и выдает ошибку
— Ашан

2
— Мне

1
Это фантастика - только одна проблема. Похоже, что моя функция запускается 5 раз, когда я использую это. Я добавил счетчик, чтобы он работал нормально, но мне просто интересно, если кто-нибудь знает, почему это произойдет ... Кажется немного странным, вы знаете?
— itchyspacesuit

22

Я должен был реализовать эту логику при работе с гугл чартами. то, что я сделал, было то, что в конце моего html внутри определения контроллера я добавил.

  <body>
         -- some html here -- 
--and at the end or where ever you want --
          <div ng-init="FunCall()"></div>
    </body>

и в этой функции просто вызовите свою логику.

$scope.FunCall = function () {
        alert("Called");
}

Я использовал это, но в верхней части страницы, инициализация требовала загрузки уже некоторого div, поэтому перемещение ng-init в конец страницы устранило мою проблему, хороший совет, и это имеет смысл.
— Gurnard

единственный ответ, который помог мне с моей проблемой (пустой выбор jquery для мобильных устройств)
— kaiser

Это реальный ответ, поскольку он адаптируется к динамически загруженным элементам управления и очень прост в реализации.
— Джейсон Себринг,

этот ответ сэкономил мне некоторое время, настолько элегантный и эффективный для моего случая использования
— Sello

4
var myM = angular.module('data-module');

myM.directive('myDirect',['$document', function( $document ){

    function link( scope , element , attrs ){

        element.ready( function(){

        } );

        scope.$on( '$viewContentLoaded' , function(){

            console.log(" ===> Called on View Load ") ;

        } );

    }

    return {
        link: link
    };

}] );

Выше метод работал на меня


3

Вы можете вызвать javascript-версию события onload в угловых js. это событие ng-load может быть применено к любому элементу dom, такому как div, span, body, iframe, img и т. д. Ниже приведена ссылка для добавления ng-load в существующий проект.

скачать ng-load для угловой js

Ниже приведен пример для iframe: после загрузки в контроллере будет вызвана функция testCallbackFunction.

ПРИМЕР

JS

    // include the `ngLoad` module
    var app = angular.module('myApp', ['ngLoad']);
    app.controller('myCtrl', function($scope) {
        $scope.testCallbackFunction = function() {
          //TODO : Things to do once Element is loaded
        };

    });  

HTML

  <div ng-app='myApp' ng-controller='myCtrl'> 
      <iframe src="test.html" ng-load callback="testCallbackFunction()">  
  </div>

Пожалуйста, добавьте пример кода, чтобы показать, как ОП может решить проблему
— Jro

@jro Обновил мой ответ. Надеюсь, что это будет полезно :)
— Даршан Джайн

2

Если вы получаете сообщение об ошибке $ digest уже в процессе, это может помочь:

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

                if(!$scope.$$phase) {
                    $scope.$apply(function(){
                        var func = $parse(attrs.elemReady);
                        func($scope);
                    })
                }
                else {

                    var func = $parse(attrs.elemReady);
                    func($scope);
                }

            })
        }
    }

0

Запуск после загрузки страницы должен быть частично удовлетворен путем установки прослушивателя события на событие загрузки окна

window.addEventListener("load",function()...)

Внутри module.run(function()...)angular у вас будет весь доступ к структуре модуля и зависимостям.

Вы можете broadcastи emitсобытия для связи мостов.

Например:

  • модуль устанавливает событие загрузки и логику сборки
  • Событие модуля транслируется на контроллеры, когда этого требует логика
  • контроллеры будут слушать и выполнять свою собственную логику, основанную на процессах загрузки модуля.

0

Я использую {{myFunction()}}в шаблоне , но затем нашел другой способ здесь , используя $timeoutвнутри контроллера. Думал, что поделюсь этим, прекрасно работает для меня.

angular.module('myApp').controller('myCtrl', ['$timeout',
function($timeout) {
var self = this;

self.controllerFunction = function () { alert('controller function');}

$timeout(function () {
    var vanillaFunction = function () { alert('vanilla function'); }();
    self.controllerFunction();
});

}]);

Я попробовал все ответы здесь и по какой-то причине $ timeout - единственное, что сработало для меня. Не знаю почему, но это может иметь какое-то отношение к ng-include и времени запуска сервисов.
— Дреллгор

Я добавил один плюс для {{myFunction ()}}
— commonpike

0

Если вы хотите, чтобы определенный элемент был полностью загружен, используйте ng-init для этого элемента.

например <div class="modal fade" id="modalFacultyInfo" role="dialog" ng-init="initModalFacultyInfo()"> ..</div>

функция initModalFacultyInfo () должна существовать в контроллере.


0

Я обнаружил, что если у вас есть вложенные представления - $ viewContentLoaded запускается для каждого из вложенных представлений. Я создал этот обходной путь, чтобы найти окончательный $ viewContentLoaded. Кажется, работает нормально для установки $ window.prerenderReady в соответствии с требованиями Prerender (входит в .run () в основном app.js):

// Trigger $window.prerenderReady once page is stable
// Note that since we have nested views - $viewContentLoaded is fired multiple
// times and we need to go around this problem
var viewContentLoads = 0;
var checkReady = function(previousContentLoads) {
  var currentContentLoads = Number(viewContentLoads) + 0; // Create a local copy of the number of loads
  if (previousContentLoads === currentContentLoads) { // Check if we are in a steady state
    $window.prerenderReady = true; // Raise the flag saying we are ready
  } else {
    if ($window.prerenderReady || currentContentLoads > 20) return; // Runaway check
    $timeout(function() {checkReady(currentContentLoads);}, 100); // Wait 100ms and recheck
  }
};
$rootScope.$on('$stateChangeSuccess', function() {
  checkReady(-1); // Changed the state - ready to listen for end of render
});
$rootScope.$on('$viewContentLoaded', function() {
  viewContentLoads ++;
});

0
var myTestApp = angular.module("myTestApp", []); 
myTestApp.controller("myTestController", function($scope, $window) {
$window.onload = function() {
 alert("is called on page load.");
};
});

Хотя этот код может ответить на вопрос, предоставление дополнительного контекста относительно того, почему и / или как этот код отвечает на вопрос, повышает его долгосрочную ценность.
— Rollstuhlfahrer

0

Решение, которое работает для меня, заключается в следующем

app.directive('onFinishRender', ['$timeout', '$parse', function ($timeout, $parse) {
    return {
        restrict: 'A',
        link: function (scope, element, attr) {
            if (scope.$last === true) {
                $timeout(function () {
                    scope.$emit('ngRepeatFinished');
                    if (!!attr.onFinishRender) {
                        $parse(attr.onFinishRender)(scope);
                    }
                });
            }

            if (!!attr.onStartRender) {
                if (scope.$first === true) {
                    $timeout(function () {
                        scope.$emit('ngRepeatStarted');
                        if (!!attr.onStartRender) {
                            $parse(attr.onStartRender)(scope);
                        }
                    });
                }
            }
        }
    }
}]);

Код контроллера следующий

$scope.crearTooltip = function () {
     $('[data-toggle="popover"]').popover();
}

HTML-код является следующим

<tr ng-repeat="item in $data" on-finish-render="crearTooltip()">

-31

Я использую, setIntervalчтобы ждать загруженного контента. Я надеюсь, что это может помочь вам решить эту проблему.

var $audio = $('#audio');
var src = $audio.attr('src');
var a;
a = window.setInterval(function(){
    src = $audio.attr('src');
    if(src != undefined){
        window.clearInterval(a);
        $('audio').mediaelementplayer({
            audioWidth: '100%'
        });
    }
}, 0);

13
Люди все еще рекомендуют setIntervalв качестве решения в 2016 году ?
— Захари Дахан

но нет простого API в agular ... как .. как это сделать?
— Петр Кула
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.