AngularJS: Як зробити сценарій кутового завантаження всередині ng-include?


85

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

Проблема в цьому.

У мене є щось подібне у моєму main.html:

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

І мій частковий.html має щось подібне

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

І мій частковий.js не має нічого спільного з angularjs. nginclude працює, і я бачу html, але я взагалі не бачу, як завантажується файл javascript. Я знаю, як використовувати firebug / chrome-dev-tool, але навіть не бачу, як робиться мережевий запит. Що я роблю не так?

Я knwo angular має якесь особливе значення для тегу сценарію. Чи можу я його замінити?

Відповіді:


101

Прийнята відповідь не буде працювати з 1.2.0-rc1 + ( випуск Github ).

Ось коротке виправлення, створене ендорамою :

/*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

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

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

39

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


2
Як це працює, якщо ваш частковий.js містить контролер? Шаблон відобразиться до завантаження контролера і видасть помилку: Аргумент 'MyController' не є функцією, визначений невизначеним.
Sthomps

2
@sthomps, подивись, чи це допомагає: Динамічне завантаження контролера
Марк Райкок,

16
Я не розумію, чому це працює - чи браузер автоматично не завантажує вставлений тег сценарію? Чому це стосується jQuery або jqLite?
Скотт Коутс

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

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

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 має свої проблеми, які не мають функції завантаження залежностей ..
Serak Shiferaw

1

Для динамічного завантаження recaptcha з 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>

це додає або перевизначає директиву кутового власного сценарію? І це використання jquery з цим?
jamie

1
Це додає. І ні, жоден Jquery там не використовується
Майкл Дрейпер

thx - 'тут не використовується jQuery' -weird- $(element)навіщо потрібен $()синтаксис. з ajs docs "Усі посилання на елементи в Angular завжди обертаються jQuery або jqLite" (наприклад, аргумент елемента у функції компіляції / посилання директиви) - отже, з цього та мого іншого досвіду - я очікував від вас використання елемента ". append (...) `- хоча в підсумку я виявився не тим, чим я міг би скористатись - під час тестування я заплутався, побачивши цю частину, і не працював для мене, коли намагався працювати лише з елементом` `element.append '' синтаксис
jamie

так, це так, але ви можете замінити це на angular.element (), якщо хочете, швидше за все.
Michael Draper

0

На жаль, усі відповіді в цій публікації для мене не спрацювали. Я постійно отримував наступну помилку.

Не вдалося виконати "запис" у "Документі": неможливо записати в документ із асинхронно завантаженого зовнішнього сценарію, якщо він явно не відкритий.

Я з’ясував, що це трапляється, якщо ви використовуєте сторонні віджети (в моєму випадку requestforce), які також викликають додаткові зовнішні файли 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.