Событие 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.