Отправка события после завершения загрузки AngularJS

114

Интересно, как лучше всего определить завершение загрузки / начальной загрузки страницы, когда все директивы завершили компиляцию / компоновку.

Уже есть какое-нибудь мероприятие? Стоит ли перегружать функцию начальной загрузки?

Лиор
источник

Ответы:

204

Просто догадка: почему бы не посмотреть, как это делает директива ngCloak? Очевидно, что директиве ngCloak удается отображать контент после загрузки. Бьюсь об заклад, просмотр ngCloak приведет к точному ответу ...

ИЗМЕНИТЬ 1 час спустя: Хорошо, я посмотрел на ngCloak, и он действительно короткий. Это, очевидно, означает, что функция компиляции не будет выполняться до тех пор, пока не будут вычислены выражения {{template}} (то есть загруженный шаблон), что обеспечивает прекрасную функциональность директивы ngCloak.

Мое обоснованное предположение заключалось в том, чтобы просто создать директиву с той же простотой, что и ngCloak, а затем в вашей функции компиляции делать все, что вы хотите. :) Поместите директиву в корневой элемент вашего приложения. Вы можете назвать директиву чем-то вроде myOnload и использовать ее как атрибут my-onload. Функция компиляции будет выполняться после компиляции шаблона (оценки выражений и загрузки подшаблонов).

РЕДАКТИРОВАТЬ, 23 часа спустя: Хорошо, я провел небольшое исследование и задал свой вопрос . Вопрос, который я задал, был косвенно связан с этим вопросом, но по совпадению привел меня к ответу, который решает этот вопрос.

Ответ заключается в том, что вы можете создать простую директиву и поместить свой код в функцию ссылки директивы, которая (для большинства случаев использования, описанных ниже) будет запускаться, когда ваш элемент будет готов / загружен. Основываясь на описании Джошем порядка, в котором выполняются функции компиляции и компоновки ,

если у вас есть такая разметка:

<div directive1>
  <div directive2>
    <!-- ... -->
  </div>
</div>

Затем AngularJS создаст директивы, запустив директивные функции в определенном порядке:

directive1: compile
  directive2: compile
directive1: controller
directive1: pre-link
  directive2: controller
  directive2: pre-link
  directive2: post-link
directive1: post-link

По умолчанию прямая функция «ссылки» является пост-ссылкой, поэтому функция ссылки вашей внешней директивы 1 не будет выполняться до тех пор, пока не будет выполнена функция ссылки внутренней директивы 2. Вот почему мы говорим, что безопасно выполнять манипуляции с DOM только в пост-ссылке. Итак, что касается исходного вопроса, не должно быть проблем с доступом к внутреннему html дочерней директивы из функции ссылки внешней директивы, хотя динамически вставляемое содержимое должно быть скомпилировано, как сказано выше.

Из этого мы можем сделать вывод, что мы можем просто создать директиву для выполнения нашего кода, когда все будет готово / скомпилировано / связано / загружено:

    app.directive('ngElementReady', [function() {
        return {
            priority: -1000, // a low number so this directive loads after all other directives have loaded. 
            restrict: "A", // attribute only
            link: function($scope, $element, $attributes) {
                console.log(" -- Element ready!");
                // do what you want here.
            }
        };
    }]);

Теперь вы можете поместить директиву ngElementReady в корневой элемент приложения, и она console.logсработает при загрузке:

<body data-ng-app="MyApp" data-ng-element-ready="">
   ...
   ...
</body>

Это так просто! Просто сделайте простую директиву и используйте ее. ;)

Вы можете дополнительно настроить его, чтобы он мог выполнять выражение (то есть функцию), добавив $scope.$eval($attributes.ngElementReady);к нему:

    app.directive('ngElementReady', [function() {
        return {
            priority: Number.MIN_SAFE_INTEGER, // execute last, after all other directives if any.
            restrict: "A",
            link: function($scope, $element, $attributes) {
                $scope.$eval($attributes.ngElementReady); // execute the expression in the attribute.
            }
        };
    }]);

Затем вы можете использовать его для любого элемента:

<body data-ng-app="MyApp" data-ng-controller="BodyCtrl" data-ng-element-ready="bodyIsReady()">
    ...
    <div data-ng-element-ready="divIsReady()">...<div>
</body>

Просто убедитесь, что ваши функции (например, bodyIsReady и divIsReady) определены в области (в контроллере), в которой находится ваш элемент.

Предостережения: я сказал, что это будет работать в большинстве случаев. Будьте осторожны при использовании определенных директив, таких как ngRepeat и ngIf. Они создают свою собственную область видимости, и ваша директива может не сработать. Например, если вы поместите нашу новую директиву ngElementReady в элемент, который также имеет ngIf, и условие ngIf оценивается как false, тогда наша директива ngElementReady не загрузится. Или, например, если вы поместите нашу новую директиву ngElementReady в элемент, который также имеет директиву ngInclude, наша директива не будет загружена, если шаблон для ngInclude не существует. Некоторые из этих проблем можно обойти, если вложить директивы вместо того, чтобы помещать их все в один и тот же элемент. Например, сделав это:

<div data-ng-element-ready="divIsReady()">
    <div data-ng-include="non-existent-template.html"></div>
<div>

вместо этого:

<div data-ng-element-ready="divIsReady()" data-ng-include="non-existent-template.html"></div>

Директива ngElementReady будет скомпилирована в последнем примере, но ее функция связывания не будет выполняться. Примечание: директивы всегда компилируются, но их функции связывания не всегда выполняются в зависимости от определенных сценариев, подобных описанному выше.

ИЗМЕНИТЬ, через несколько минут:

Да, и чтобы полностью ответить на вопрос, вы можете теперь $emitили $broadcastсвое событие из выражения или функции, которая выполняется в ng-element-readyатрибуте. :) Например:

<div data-ng-element-ready="$emit('someEvent')">
    ...
<div>

ИЗМЕНИТЬ, даже через несколько минут:

Ответ @ satchmorun тоже работает, но только для начальной загрузки. Вот очень полезный вопрос SO, который описывает порядок выполнения вещей, включая функции ссылок app.run, и другие. Так что, в зависимости от вашего варианта использования, это app.runможет быть хорошо, но не для конкретных элементов, и в этом случае функции ссылок лучше.

РЕДАКТИРОВАТЬ, пять месяцев спустя, 17 октября в 8:11 по тихоокеанскому времени:

Это не работает с частичными файлами, которые загружаются асинхронно. Вам нужно будет добавить бухгалтерию в свои частичные файлы (например, один из способов - заставить каждую частичную часть отслеживать, когда ее содержимое загружается, а затем генерировать событие, чтобы родительская область могла подсчитать, сколько частей было загружено, и, наконец, сделать то, что ей нужно для делать после загрузки всех партиалов).

ИЗМЕНИТЬ, 23 октября в 22:52 по тихоокеанскому стандартному времени:

Я сделал простую директиву для запуска некоторого кода при загрузке изображения:

/*
 * This img directive makes it so that if you put a loaded="" attribute on any
 * img element in your app, the expression of that attribute will be evaluated
 * after the images has finished loading. Use this to, for example, remove
 * loading animations after images have finished loading.
 */
  app.directive('img', function() {
    return {
      restrict: 'E',
      link: function($scope, $element, $attributes) {
        $element.bind('load', function() {
          if ($attributes.loaded) {
            $scope.$eval($attributes.loaded);
          }
        });
      }
    };
  });

РЕДАКТИРОВАТЬ, 24 октября в 12:48 по тихоокеанскому времени:

Я улучшил свою исходную ngElementReadyдирективу и переименовал ее в whenReady.

/*
 * The whenReady directive allows you to execute the content of a when-ready
 * attribute after the element is ready (i.e. done loading all sub directives and DOM
 * content except for things that load asynchronously like partials and images).
 *
 * Execute multiple expressions by delimiting them with a semi-colon. If there
 * is more than one expression, and the last expression evaluates to true, then
 * all expressions prior will be evaluated after all text nodes in the element
 * have been interpolated (i.e. {{placeholders}} replaced with actual values). 
 *
 * Caveats: if other directives exists on the same element as this directive
 * and destroy the element thus preventing other directives from loading, using
 * this directive won't work. The optimal way to use this is to put this
 * directive on an outer element.
 */
app.directive('whenReady', ['$interpolate', function($interpolate) {
  return {
    restrict: 'A',
    priority: Number.MIN_SAFE_INTEGER, // execute last, after all other directives if any.
    link: function($scope, $element, $attributes) {
      var expressions = $attributes.whenReady.split(';');
      var waitForInterpolation = false;

      function evalExpressions(expressions) {
        expressions.forEach(function(expression) {
          $scope.$eval(expression);
        });
      }

      if ($attributes.whenReady.trim().length == 0) { return; }

      if (expressions.length > 1) {
        if ($scope.$eval(expressions.pop())) {
          waitForInterpolation = true;
        }
      }

      if (waitForInterpolation) {
        requestAnimationFrame(function checkIfInterpolated() {
          if ($element.text().indexOf($interpolate.startSymbol()) >= 0) { // if the text still has {{placeholders}}
            requestAnimationFrame(checkIfInterpolated);
          }
          else {
            evalExpressions(expressions);
          }
        });
      }
      else {
        evalExpressions(expressions);
      }
    }
  }
}]);

Например, используйте его так, чтобы срабатывать, someFunctionкогда элемент загружен и {{placeholders}}еще не заменен:

<div when-ready="someFunction()">
  <span ng-repeat="item in items">{{item.property}}</span>
</div>

someFunctionбудет вызываться перед item.propertyзаменой всех заполнителей.

Вычислите столько выражений, сколько хотите, и сделайте последнее выражение, trueожидающее {{placeholders}}вычисления, следующим образом:

<div when-ready="someFunction(); anotherFunction(); true">
  <span ng-repeat="item in items">{{item.property}}</span>
</div>

someFunctionи anotherFunctionбудет уволен после {{placeholders}}замены.

Это работает только при первой загрузке элемента, но не при будущих изменениях. Это может не работать должным образом, если a $digestпродолжает происходить после первоначальной замены заполнителей (дайджест $ может произойти до 10 раз, пока данные не перестанут изменяться). Он подойдет для подавляющего большинства случаев использования.

РЕДАКТИРОВАТЬ, 31 октября в 19:26 по тихоокеанскому стандартному времени:

Хорошо, это, наверное, мое последнее и последнее обновление. Это, вероятно, будет работать для 99,999 случаев использования:

/*
 * The whenReady directive allows you to execute the content of a when-ready
 * attribute after the element is ready (i.e. when it's done loading all sub directives and DOM
 * content). See: /programming/14968690/sending-event-when-angular-js-finished-loading
 *
 * Execute multiple expressions in the when-ready attribute by delimiting them
 * with a semi-colon. when-ready="doThis(); doThat()"
 *
 * Optional: If the value of a wait-for-interpolation attribute on the
 * element evaluates to true, then the expressions in when-ready will be
 * evaluated after all text nodes in the element have been interpolated (i.e.
 * {{placeholders}} have been replaced with actual values).
 *
 * Optional: Use a ready-check attribute to write an expression that
 * specifies what condition is true at any given moment in time when the
 * element is ready. The expression will be evaluated repeatedly until the
 * condition is finally true. The expression is executed with
 * requestAnimationFrame so that it fires at a moment when it is least likely
 * to block rendering of the page.
 *
 * If wait-for-interpolation and ready-check are both supplied, then the
 * when-ready expressions will fire after interpolation is done *and* after
 * the ready-check condition evaluates to true.
 *
 * Caveats: if other directives exists on the same element as this directive
 * and destroy the element thus preventing other directives from loading, using
 * this directive won't work. The optimal way to use this is to put this
 * directive on an outer element.
 */
app.directive('whenReady', ['$interpolate', function($interpolate) {
  return {
    restrict: 'A',
    priority: Number.MIN_SAFE_INTEGER, // execute last, after all other directives if any.
    link: function($scope, $element, $attributes) {
      var expressions = $attributes.whenReady.split(';');
      var waitForInterpolation = false;
      var hasReadyCheckExpression = false;

      function evalExpressions(expressions) {
        expressions.forEach(function(expression) {
          $scope.$eval(expression);
        });
      }

      if ($attributes.whenReady.trim().length === 0) { return; }

    if ($attributes.waitForInterpolation && $scope.$eval($attributes.waitForInterpolation)) {
        waitForInterpolation = true;
    }

      if ($attributes.readyCheck) {
        hasReadyCheckExpression = true;
      }

      if (waitForInterpolation || hasReadyCheckExpression) {
        requestAnimationFrame(function checkIfReady() {
          var isInterpolated = false;
          var isReadyCheckTrue = false;

          if (waitForInterpolation && $element.text().indexOf($interpolate.startSymbol()) >= 0) { // if the text still has {{placeholders}}
            isInterpolated = false;
          }
          else {
            isInterpolated = true;
          }

          if (hasReadyCheckExpression && !$scope.$eval($attributes.readyCheck)) { // if the ready check expression returns false
            isReadyCheckTrue = false;
          }
          else {
            isReadyCheckTrue = true;
          }

          if (isInterpolated && isReadyCheckTrue) { evalExpressions(expressions); }
          else { requestAnimationFrame(checkIfReady); }

        });
      }
      else {
        evalExpressions(expressions);
      }
    }
  };
}]);

Используйте это так

<div when-ready="isReady()" ready-check="checkIfReady()" wait-for-interpolation="true">
   isReady will fire when this {{placeholder}} has been evaluated
   and when checkIfReady finally returns true. checkIfReady might
   contain code like `$('.some-element').length`.
</div>

Конечно, его, вероятно, можно оптимизировать, но на этом я остановлюсь. requestAnimationFrame хорош.

trusktr
источник
3
Действительно раздражает всеми этими префиксами "data-". Рад, что сам ими не пользуюсь.
stolsvik 09
1
@stolsvik хех, ага, в самых современных браузерах они не нужны.
trusktr
49
Заслуживает голоса за количество времени и усилий, вложенных в этот ответ. Хорошая работа!
GordyD
8
Хороший ответ, но, пожалуйста, подумайте об удалении всех строк «Изменить» и немного измените структуру ответа. История редактирования доступна по ссылке «отредактировано ...» внизу вашего ответа, и она отвлекает во время чтения.
user247702
2
Исходный код был бы действительно полезен. И если вы можете сделать это общедоступным на npm, это будет ИДЕАЛЬНО. Действительно хороший ответ, действительно красиво объясненный, +1 за количество усилий, приложенных к этому.
tfrascaroli
38

В документации дляangular.Module есть запись, описывающая runфункцию:

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

Итак, если у вас есть модуль, который является вашим приложением:

var app = angular.module('app', [/* module dependencies */]);

Вы можете запускать вещи после загрузки модулей:

app.run(function() {
  // Do post-load initialization stuff here
});

РЕДАКТИРОВАТЬ: ручная инициализация в помощь

Итак, было указано, что runне вызывается, когда DOM готов и подключен. Он вызывается, когда $injectorмодуль, на который указывает ссылка, ng-appзагружает все свои зависимости, что является отдельным от этапа компиляции DOM.

Я еще раз взглянул на ручную инициализацию , и, похоже, это должно помочь.

Я сделал скрипку для иллюстрации .

HTML прост:

<html>
    <body>
        <test-directive>This is a test</test-directive>
    </body>
</html>

Обратите внимание на отсутствие файла ng-app. И у меня есть директива, которая будет выполнять некоторые манипуляции с DOM, чтобы мы могли следить за порядком и временем действий.

Как обычно, создается модуль:

var app = angular.module('app', []);

А вот и директива:

app.directive('testDirective', function() {
    return {
        restrict: 'E',
        template: '<div class="test-directive"><h1><div ng-transclude></div></h1></div>',
        replace: true,
        transclude: true,
        compile: function() {
            console.log("Compiling test-directive");
            return {
                pre: function() { console.log("Prelink"); },
                post: function() { console.log("Postlink"); }
            };
        }
    };
});

Мы собираемся заменить test-directiveтег на a divof class test-directiveи обернуть его содержимое в h1.

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

Вот остальной код:

// The bootstrapping process

var body = document.getElementsByTagName('body')[0];

// Check that our directive hasn't been compiled

function howmany(classname) {
    return document.getElementsByClassName(classname).length;
}

Прежде чем мы что-либо сделаем, test-directiveв DOM не должно быть элементов с классом , а после того, как мы закончим, должно быть 1.

console.log('before (should be 0):', howmany('test-directive'));

angular.element(document).ready(function() {
    // Bootstrap the body, which loades the specified modules
    // and compiled the DOM.
    angular.bootstrap(body, ['app']);

    // Our app is loaded and the DOM is compiled
    console.log('after (should be 1):', howmany('test-directive'));
});

Это довольно просто. Когда документ будет готов, вызовите его angular.bootstrapс корневым элементом вашего приложения и массивом имен модулей.

Фактически, если вы прикрепите runфункцию к appмодулю , вы увидите, что она запускается до того, как произойдет какая-либо компиляция.

Если вы запустите скрипт и посмотрите на консоль, вы увидите следующее:

before (should be 0): 0 
Compiling test-directive 
Prelink
Postlink
after (should be 1): 1 <--- success!
satchmorun
источник
2
спасибо @satchmorun! но run () выполняется до конца части связывания - просто проверил это с помощью некоторых console.logs.
Лиор
Мне самому было любопытно ... У меня есть директива, которая запускает некоторые плагины jQuery DOM, run срабатывает перед директивой, а при запуске срабатывает html - это еще не все
charlietfl
@charlietfl - Я немного углубился в ручную загрузку, и на самом деле это довольно простой способ получить то, что ищет вопрос. Я добавил довольно длинную правку к своему исходному ответу.
satchmorun
5
я обнаружил, что использование $timeout( initMyPlugins,0)работает в моей директиве, весь HTML, который мне нужен, есть
charlietfl
@satchmorun, см. это продолжение: stackoverflow.com/questions/14989161/…
Лиор
16

Angular не предоставил способ сигнализировать о завершении загрузки страницы, возможно потому, что «завершение» зависит от вашего приложения . Например, если у вас есть иерархическое дерево партиалов, одно загружает другие. «Готово» будет означать, что все они загружены. Любому фреймворку будет трудно проанализировать ваш код и понять, что все сделано или все еще ждут. Для этого вам нужно будет предоставить логику для конкретного приложения, чтобы проверить и определить это.

Лиор
источник
15

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

Директива:

.directive('initialisation',['$rootScope',function($rootScope) {
            return {
                restrict: 'A',
                link: function($scope) {
                    var to;
                    var listener = $scope.$watch(function() {
                        clearTimeout(to);
                        to = setTimeout(function () {
                            console.log('initialised');
                            listener();
                            $rootScope.$broadcast('initialised');
                        }, 50);
                    });
                }
            };
        }]);

Затем это можно просто добавить как атрибут к bodyэлементу, а затем прослушать для использования$scope.$on('initialised', fn)

Он работает, предполагая, что приложение инициализируется, когда больше нет циклов $ digest. $ watch вызывается каждый цикл дайджеста, и поэтому запускается таймер (setTimeout, а не $ timeout, поэтому новый цикл дайджеста не запускается). Если цикл дайджеста не происходит в течение тайм-аута, предполагается, что приложение инициализировано.

Очевидно, что это не так точно, как решение satchmoruns (возможно, цикл дайджеста занимает больше времени, чем тайм-аут), но мое решение не требует, чтобы вы отслеживали модули, что значительно упрощает управление (особенно для более крупных проектов ). В любом случае, кажется, достаточно точным для моих требований. Надеюсь, поможет.

Тео
источник
Отличное решение. Для проектов, когда весь код в одном или двух сжатых файлах работает очень хорошо.
merqlove
1
Это фантастическое решение. Если у вас много кода в jquery и вы пытаетесь шаг за шагом преобразовать код в angular, это имеет смысл.
Mangesh Pimpalkar
11

Если вы используете Angular UI Router , вы можете прослушивать $viewContentLoadedсобытие.

«$ viewContentLoaded - запускается после загрузки представления, после рендеринга DOM . '$ scope' представления генерирует событие». - Ссылка

$scope.$on('$viewContentLoaded', 
function(event){ ... });
Джордан Сколе
источник
3
$ scope. $ watch ('$ viewContentLoaded', function () помогли мне
Людовик XIV
2
проголосовали против того, «кем вы должны быть». Что, если бы я сказал: «Если вы используете React вместо Angular (как вам следовало бы) ...»? Не очень отношение иметь к этой экосистеме ИМХО.
Valentin Waeselynck
@ValentinWaeselynck, вы абсолютно правы. Я отредактировал свой ответ, чтобы убрать предвзятость.
Джордан Скол
1
Сработало у меня! Спасибо. Я фактически добавил его в свою функцию запуска, а затем изменил $ scope на $ rootScope.
JDavies
2
Как указал Angular University в другом ответе, $ viewContentLoaded, возможно, изначально не было, но теперь он работает во встроенном провайдере ngRoute точно так же. Имея это в виду, я думаю, что это быстрый, простой и читаемый ответ, который будут искать многие (большинство?) Будущих читателей.
Кевин Крамли
3

Я наблюдаю за DOM-манипуляциями angular с помощью JQuery, и я установил финиш для своего приложения (своего рода предопределенная и удовлетворительная ситуация, которая мне нужна для моего приложения-аннотации), например, я ожидаю, что мой ng-Repeater выдаст 7 результатов, а там для i установит для этого функцию наблюдения с помощью setInterval.

$(document).ready(function(){

  var interval = setInterval(function(){

  if($("article").size() == 7){
     myFunction();
     clearInterval(interval);
  }

  },50);

});
Хасан Гилак
источник
3
Я бы не стал этого делать. Использование интервалов для проверки того, что происходит, не является хорошей практикой, не масштабируется, и есть другие способы заставить что-то происходить. Таймеры предназначены для выполнения конкретных задач, которые должны произойти через определенный период времени, а не для «угадывания», когда контент или результаты готовы.
dudewad 05
Не говоря уже о том, что использование таймера jquery против платформы angular контрпродуктивно - у angular есть класс тайм-аута, и вы должны использовать его, иначе вы пересекаете две структуры, и это очень быстро сбивает с толку.
dudewad 05
3

Если вы не используете модуль ngRoute , т.е. у вас нет $ viewContentLoaded события .

Вы можете использовать другой метод директивы:

    angular.module('someModule')
        .directive('someDirective', someDirective);

    someDirective.$inject = ['$rootScope', '$timeout']; //Inject services

    function someDirective($rootScope, $timeout){
        return {
            restrict: "A",
            priority: Number.MIN_SAFE_INTEGER, //Lowest priority
            link    : function(scope, element, attr){
                $timeout(
                    function(){
                        $rootScope.$emit("Some:event");
                    }
                );
            }
        };
    }

Согласно ответу trusktr он имеет самый низкий приоритет. Плюс $ timeout заставит Angular пройти весь цикл событий перед выполнением обратного вызова.

$ rootScope используется, потому что он позволяет размещать директиву в любой области приложения и уведомлять только необходимых слушателей.

$ rootScope. $ emit запускает событие для всех $ rootScope. $ только на слушателях. Интересно то, что $ rootScope. $ Broadcast будет уведомлять все $ rootScope. $ On, а также $ scope. $ На слушателях Источник

Вадим П.
источник
2

Согласно команде Angular и этой проблеме Github :

теперь у нас есть события $ viewContentLoaded и $ includeContentLoaded, которые генерируются в ng-view и ng-include соответственно. Я думаю, что это настолько близко, насколько можно понять, когда мы закончим компиляцию.

Исходя из этого, кажется, что в настоящее время это невозможно сделать надежным способом, иначе Angular предоставил бы событие из коробки.

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

Согласно проектной документации Angular 2 :

Из-за множества дайджестов невозможно определить и уведомить компонент о том, что модель стабильна. Это связано с тем, что уведомление может дополнительно изменить данные, что может перезапустить процесс привязки.

В соответствии с этим тот факт, что это невозможно, является одной из причин, по которой было принято решение о переписывании в Angular 2.

Угловой университет
источник
2

У меня был фрагмент, который загружался после / основной партией, пришедшей через маршрутизацию.

Мне нужно было запустить функцию после того, как эта неполная часть загружена, и я не хотел писать новую директиву и понял, что можно использовать дерзкий ngIf

Контроллер родительского партиала:

$scope.subIsLoaded = function() { /*do stuff*/; return true; };

HTML неполного

<element ng-if="subIsLoaded()"><!-- more html --></element>
Hashbrown
источник
1

Если вы хотите сгенерировать JS с данными на стороне сервера (JSP, PHP), вы можете добавить свою логику в службу, которая будет загружаться автоматически при загрузке вашего контроллера.

Кроме того, если вы хотите отреагировать, когда все директивы завершены компиляцией / компоновкой, вы можете добавить соответствующие предлагаемые решения выше в логику инициализации.

module.factory('YourControllerInitService', function() {

    // add your initialization logic here

    // return empty service, because it will not be used
    return {};
});


module.controller('YourController', function (YourControllerInitService) {
});
Николай Георгиев
источник
0

Все это отличные решения, однако, если вы в настоящее время используете маршрутизацию, я считаю, что это решение является самым простым и минимальным объемом кода. Использование свойства «resolve» для ожидания выполнения обещания перед запуском маршрута. например

$routeProvider
.when("/news", {
    templateUrl: "newsView.html",
    controller: "newsController",
    resolve: {
        message: function(messageService){
            return messageService.getMessage();
    }
}

})

Щелкните здесь для просмотра полной документации - Кредит К. Скотту Аллену

aholtry
источник
0

может быть я могу помочь тебе этим примером

В настраиваемом fancybox я показываю содержимое с интерполированными значениями.

в сервисе, в "открытом" методе fancybox, я делаю

open: function(html, $compile) {
        var el = angular.element(html);
     var compiledEl = $compile(el);
        $.fancybox.open(el); 
      }

$ compile возвращает скомпилированные данные. вы можете проверить скомпилированные данные

Шайлендра Патхак
источник