Подтвердить что ты не робот

Внедрить новый angular js-контроллер с html динамически после загрузки страницы

Я работаю над сайтом angular js, который получает данные из webservice/api. Один api возвращает html и angular js код для динамического добавления компонента или любого нового нового компонента angular, который мы хотим добавить. Эта строка появится в ответе api

<div id="homecontainer" class="flex-center p-page" loader style="overflow:hidden;">
    <div class="column-1">
        <div class="grid m-0 col-xs-12">
            <div ng-repeat="Widget in V3Widgets track by $index" class="grid-item">
                <div class="grid-sizer"></div>
             {{Widget}}
            </div>


        </div>
        <div ng-controller="WelcomeController">
            {{greeting}}
        </div>
        <script>
            var app = angular.module('demo', [])
                //RestService is Other Module Which is Already Working fine 
            .controller('WelcomeController', function ($scope,RestService) {
                $scope.greeting = 'Welcome!';
            });
            angular.bootstrap(document, ['demo']);
        </script>
    </div>
</div>

Теперь у меня есть директива для привязки этой строки к странице

<renderdynamicwidgethtml ng-if="Widget.Id==null && Widget.Html!=null" html="Widget.Html"/>

Директива js

.directive('renderdynamicwidgethtml', ['$compile', function ($compile) {
    return function (scope, element, attrs) {
        scope.$watch(
          function (scope) {
              return scope.$eval(attrs.html);
          },
          function (value) {
              element.html(value);
              $compile(element.contents())(scope);
          }
       );
    };
}])

scope.$eval должен преобразовать строку в angular компоненты, но с этой ошибкой не удалось.

[ng: btstrpd] http://errors.angularjs.org/1.3.17/ng/btstrpd?p0=document

4b9b3361

Ответ 1

Так как Angular 1.5, есть новый инструмент angular.component. В этом ответе используется слово Компонент, чтобы сделать ссылку на все возможные инструменты Angular (например, директивы, фильтры и т.д.), Включая angular.component.

Сначала позвольте мне начать с того, как AngularJS устанавливает все:

  • Когда загружаются файлы javascript, вы можете видеть, что весь код Angular начинается с angular.module, angular.controller, angular.directive и т.д. И все они получают функцию как параметр (кроме angular.module, который получает имя и список зависимостей). На данный момент эти компоненты не создаются, просто зарегистрированы.

  • После того, как Angular зарегистрировал все модули и компоненты, он готов быть загружен, либо с директивой ng-app, либо вручную с помощью angular.bootstrap. Оба метода получают строку как параметр, который является именем корневого модуля. Используя этот корневой модуль, Angular глубоко заглядывает в его зависимости (который можно рассматривать как дерево зависимостей) и начинает загрузку компонентов из него (модули, которые не имеют зависимостей) до компонентов корневого модуля.

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

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

Ошибка означает, что вы дважды загружаете свое приложение. Это может произойти при использовании директивы ng-app и вызова angular.bootstrap или просто вызова angular.bootstrap более одного раза.

Как говорится в документации в разделе инициализации вручную:

Вы должны позвонить angular.bootstrap() после загрузки или определения ваши модули. Вы не можете добавлять контроллеры, службы, директивы и т.д. после загрузки приложения.

Итак, чтобы динамически загружать новые контроллеры, вы должны зарегистрировать их перед процессом начальной загрузки. Вы можете сделать это, извлекая нужные данные из вашего API (вам понадобится любой другой инструмент, такой как JQuery, поскольку Angular еще не настроен), выполняя код со стандартной функцией JavaScript eval (сделайте это внутри запрос обещания), а затем bootstraping AngularJS вручную с помощью angular.bootstrap.

Для этого код, который вы выполняете с eval, должен быть чисто javascript, я рекомендую отделить его от HTML либо путем изменения ответа API (если это возможно), либо сделать это программно после получения ответа.

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

Ответ 2

Почему вы используете scope.$eval вместо службы $compile, которая предназначена для компиляции вашей строки шаблона.

   link: function (scope, ele, attrs) {
      scope.$watch(attrs.html, function(html) {
        $compile(ele.contents())(scope);
      });
    }

Ответ 3

Попробуйте следующее:

    app.directive('dynamic', [ '$compile',
function ($compile) {
    return {
        restrict: 'A',
        replace: true,
        link: function (scope, ele, attrs) {
            scope.$watch(attrs.dynamic, function (html) {
                ele.html(html);
                $compile(ele.contents())(scope);
            });
        }
    };
}]);

Вы можете поместить всю строку в $scope.myString в js и в HTML <div dynamic="myString"></div>. Он должен компилировать и отображать все.

Ответ 4

Я нашел возможное решение, где мне не нужно знать о контроллере перед загрузкой:

// Make module Foo and store $controllerProvider in a global
var controllerProvider = null;
angular.module('Foo', [], function($controllerProvider) {
    controllerProvider = $controllerProvider;
});
// Bootstrap Foo
angular.bootstrap($('body'), ['Foo']);

// .. time passes ..

// Load javascript file with Ctrl controller
angular.module('Foo').controller('Ctrl', function($scope, $rootScope) {
    $scope.msg = "It works! rootScope is " + $rootScope.$id +
        ", should be " + $('body').scope().$id;
});
// Load html file with content that uses Ctrl controller
$('<div id="ctrl" ng-controller="Ctrl" ng-bind="msg">').appendTo('body');

// Register Ctrl controller manually
// If you can reference the controller function directly, just run:
// $controllerProvider.register(controllerName, controllerFunction);
// Note: I haven't found a way to get $controllerProvider at this stage
//    so I keep a reference from when I ran my module config
function registerController(moduleName, controllerName) {
    // Here I cannot get the controller function directly so I
    // need to loop through the module _invokeQueue to get it
    var queue = angular.module(moduleName)._invokeQueue;
    for(var i=0;i<queue.length;i++) {
        var call = queue[i];
        if(call[0] == "$controllerProvider" &&
           call[1] == "register" &&
           call[2][0] == controllerName) {
            controllerProvider.register(controllerName, call[2][1]);
        }
    }
}
registerController("Foo", "Ctrl");
// compile the new element
$('body').injector().invoke(function($compile, $rootScope) {
    $compile($('#ctrl'))($rootScope);
    $rootScope.$apply();
});

Fiddle. Единственная проблема заключается в том, что вам нужно хранить $controllerProvider и использовать его в том месте, где он действительно не должен использоваться (после загрузки). Также нет простого способа получить функцию, используемую для определения контроллера, пока он не будет зарегистрирован, поэтому мне нужно пройти через модуль _invokeQueue, который недокументирован.

ОБНОВЛЕНИЕ: для регистрации директив и сервисов вместо $controllerProvider.register просто используйте $compileProvider.directive и $provided.factory соответственно. Опять же, вам нужно сохранить ссылки на них в первоначальной конфигурации модуля.

UDPATE 2: Здесь скрипка, которая автоматически регистрирует все загружаемые контроллеры/директивы/службы без необходимости их индивидуального указания.

Ответ 5

Вы можете использовать javascript eval для кода eval js и $compile для html-кода. Я установил плункер с полным тестовым кодом. вот ссылка.

https://plnkr.co/edit/9sJw8ua1n5ItnHONwGmS