AngularJS: как сделать скрипт угловой загрузки внутри ng-include?


85

Привет, я создаю веб-страницу с помощью angular. Проблема в том, что есть кое-что, уже построенное без angular, и я также должен включить их

Проблема вот в чем.

В моем main.html есть что-то вроде этого:

<ngInclude src="partial.html">
</ngInclude>

И в моем partial.html есть что-то вроде этого

<h2> heading 1 <h2>
<script type="text/javascript" src="static/js/partial.js">
</script>

И мой partial.js не имеет ничего общего с angularjs. nginclude работает, и я вижу html, но я не вижу загружаемого файла javascript. Я знаю, как использовать firebug / chrome-dev-tool, но я даже не вижу выполняемого сетевого запроса. Что я делаю не так?

Я знаю, что angular имеет особое значение для тега script. Могу я отменить это?

Ответы:


101

Принятый ответ не будет работать с 1.2.0-rc1 + ( проблема с Github ).

Вот быстрое исправление, созданное endorama :

/*global angular */
(function (ng) {
  'use strict';

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

  app.directive('script', function() {
    return {
      restrict: 'E',
      scope: false,
      link: function(scope, elem, attr) {
        if (attr.type === 'text/javascript-lazy') {
          var code = elem.text();
          var f = new Function(code);
          f();
        }
      }
    };
  });

}(angular));

Просто добавьте этот файл, загрузите ngLoadScriptмодуль как зависимость приложения и используйте его type="text/javascript-lazy"как тип для скрипта, который нужно загружать лениво по частям:

<script type="text/javascript-lazy">
  console.log("It works!");
</script>

5
Я не думаю, что это сработает, если ваш тег скрипта имеет атрибут src, а не встроенный js.
Blaskovicz


1
Это работает отлично, но я хочу еще кое-что. Как мне удалить это после уничтожения директивы?
SLearner 06

Когда я объявляю контроллер в ленивом скрипте, я получаю ошибку при обращении к контроллеру в представлении. Ты знаешь почему?
Changwang Zhang

Это сработало. Я использовал его для запуска кода инициализации вкладок для набора вкладок, загруженного в частичную часть
LKallipo

39

Краткий ответ: AngularJS ("jqlite") этого не поддерживает. Включите jQuery на свою страницу (перед включением Angular), и он должен работать. См. Https://groups.google.com/d/topic/angular/H4haaMePJU0/discussion


2
Как это работает, если ваш partial.js содержит контроллер? Шаблон будет отображаться до загрузки контроллера и выдаст ошибку: аргумент MyController не является функцией, он не определен.
Sthomps

2
@sthomps, посмотрите, поможет ли это: динамическая загрузка контроллера
Марк Райкок,

16
Я не понимаю, почему это работает - браузер не загружает автоматически внедренный тег скрипта? Почему это вызывает беспокойство jQuery или jqLite?
Скотт Коутс,

сработал и у меня, содержимое моего тега <script> не было связано с angularjs. Когда я не загружал jQuery до angularjs, все в моей части ниже, включая тег script, было удалено, что очень странно.
Ник Расслер,

20

Я пробовал подход neemzy, но у меня не получилось использовать 1.2.0-rc.3. Тег скрипта будет вставлен в DOM, но путь javascript не будет загружен. Я подозреваю, что это было потому, что javascript, который я пытался загрузить, был из другого домена / протокола. Поэтому я выбрал другой подход, и вот что я придумал, используя в качестве примера карты Google: ( Gist )

angular.module('testApp', []).
    directive('lazyLoad', ['$window', '$q', function ($window, $q) {
        function load_script() {
            var s = document.createElement('script'); // use global document since Angular's $document is weak
            s.src = 'https://maps.googleapis.com/maps/api/js?sensor=false&callback=initialize';
            document.body.appendChild(s);
        }
        function lazyLoadApi(key) {
            var deferred = $q.defer();
            $window.initialize = function () {
                deferred.resolve();
            };
            // thanks to Emil Stenström: http://friendlybit.com/js/lazy-loading-asyncronous-javascript/
            if ($window.attachEvent) {  
                $window.attachEvent('onload', load_script); 
            } else {
                $window.addEventListener('load', load_script, false);
            }
            return deferred.promise;
        }
        return {
            restrict: 'E',
            link: function (scope, element, attrs) { // function content is optional
            // in this example, it shows how and when the promises are resolved
                if ($window.google && $window.google.maps) {
                    console.log('gmaps already loaded');
                } else {
                    lazyLoadApi().then(function () {
                        console.log('promise resolved');
                        if ($window.google && $window.google.maps) {
                            console.log('gmaps loaded');
                        } else {
                            console.log('gmaps not loaded');
                        }
                    }, function () {
                        console.log('promise rejected');
                    });
                }
            }
        };
    }]);

Надеюсь, это кому-то поможет.


4
Странно, я сделал свою версию на 1.2.0-rc3 с внешними скриптами :) Ваша версия в любом случае лучше, спасибо, что поделились этим!
neemzy 03

12

Я использовал этот метод для динамической загрузки файла сценария (внутри контроллера).

var script = document.createElement('script');
script.type = 'text/javascript';
script.src = "https://maps.googleapis.com/maps/api/js";
document.body.appendChild(script);

nice one @azmeer
Али

Проголосовал, потому что это простое и лучшее решение для динамической загрузки скриптов.
RLD

8

Это больше не будет работать с 1.2.0-rc1. См. Дополнительную информацию об этой проблеме , в которой я опубликовал комментарий, описывающий быстрое решение. Я тоже поделюсь этим здесь:

// Quick fix : replace the script tag you want to load by a <div load-script></div>.
// Then write a loadScript directive that creates your script tag and appends it to your div.
// Took me one minute.

// This means that in your view, instead of :
<script src="/path/to/my/file.js"></script>

// You'll have :
<div ng-load-script></div>

// And then write a directive like :
angular.module('myModule', []).directive('loadScript', [function() {
    return function(scope, element, attrs) {
        angular.element('<script src="/path/to/my/file.js"></script>').appendTo(element);
    }
}]);

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


4

ocLazyLoad позволяет лениво загружать скрипты в шаблоны / представления через маршрутизаторы (например, ui-router). Вот снайплет

$stateProvider.state('parent', {
    url: "/",
    resolve: {
        loadMyService: ['$ocLazyLoad', function($ocLazyLoad) {
             return $ocLazyLoad.load('js/ServiceTest.js');
        }]
    }
})
.state('parent.child', {
    resolve: {
        test: ['loadMyService', '$ServiceTest', function(loadMyService, $ServiceTest) {
            // you can use your service
            $ServiceTest.doSomething();
        }]
    }
});  

Я столкнулся с такой же проблемой. в корпоративном приложении вы должны использовать все свои маршруты, поэтому лучше использовать ng-include с вкладками, но у ng-include есть свои проблемы, заключающиеся в том, что у него нет функции загрузки зависимостей ..
Серак Шиферау,

1

Для динамической загрузки рекапчи из a ui-viewя использую следующий метод:

В application.js:

    .directive('script', function($parse, $rootScope, $compile) {
    return {
        restrict: 'E',
        terminal: true,
        link: function(scope, element, attr) {
            if (attr.ngSrc) {
                 var domElem = '<script src="'+attr.ngSrc+'" async defer></script>';
                 $(element).append($compile(domElem)(scope));


            }
        }
    };
});

В myPartial.client.view.html:

 <script type="application/javascript" ng-src="http://www.google.com/recaptcha/api.js?render=explicit&onload=vcRecaptchaApiLoaded"></script>

это добавляет или отменяет директиву angular native script? И это с помощью jquery?
jamie

1
Это добавляет к. И нет, там не используется JQuery
Майкл Дрейпер,

спасибо - 'здесь не используется jQuery' -weird- $(element)зачем тебе $()синтаксис. из документов ajs «Все ссылки на элементы в Angular всегда обертываются с помощью jQuery или jqLite» (например, аргумент элемента в функции компиляции / связывания директивы) - так что, исходя из этого и других моих опытов - я ожидал, что вы будете использовать элемент. append (...) `- хотя в итоге я оказался не тем, что мог бы использовать - при тестировании я был сбит с толку, когда увидел эту часть, и не работал у меня, когда я пытался, работал только с 'element.append' синтаксис
jamie

doh да, но вы можете заменить это на angular.element (), если хотите, скорее всего.
Майкл Дрейпер

0

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

Не удалось выполнить «запись» в «Документе»: невозможно выполнить запись в документ из асинхронно загруженного внешнего скрипта, если он не открыт явно.

Я обнаружил, что это происходит, если вы используете какие-то сторонние виджеты (в моем случае - requireforce), которые также вызывают дополнительные внешние файлы JavaScript и пытаются вставить HTML. Глядя на консоль и код JavaScript, я заметил несколько таких строк:

document.write("<script type='text/javascript' "..."'></script>");

Я использовал сторонние файлы JavaScript (htmlParser.js и postscribe.js) из: https://github.com/krux/postscribe . Это решило проблему в этом посте и одновременно исправило указанную выше ошибку.

(Это был быстрый и грязный путь в сжатые сроки, которые у меня сейчас есть. Однако мне неудобно использовать стороннюю библиотеку JavaScript. Я надеюсь, что кто-то сможет придумать более чистый и лучший способ.)


0

Я пробовал явно использовать Google reCAPTCHA. Вот пример:

// put somewhere in your index.html
<script type="text/javascript">
var onloadCallback = function() {
  grecaptcha.render('your-recaptcha-element', {
    'sitekey' : '6Ldcfv8SAAAAAB1DwJTM6T7qcJhVqhqtss_HzS3z'
  });
};

//link function of Angularjs directive
link: function (scope, element, attrs) {
  ...
  var domElem = '<script src="https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit" async defer></script>';
  $('#your-recaptcha-element').append($compile(domElem)(scope));
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.