AngularJS: Jak zrobić skrypt ładowania kątowego wewnątrz ng-include?

85

Hej, buduję stronę internetową z angular. Problem w tym, że coś jest już zbudowane bez kątowników i też muszę je uwzględnić

Problem w tym.

Mam coś takiego w moim main.html:

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

A mój Partial.html ma coś takiego

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

A mój Partial.js nie ma nic wspólnego z angularjs. nginclude działa i widzę html, ale nie widzę w ogóle ładowanego pliku javascript. Wiem, jak używać narzędzia firebug / chrome-dev-tool, ale nie widzę nawet wysyłanego żądania sieciowego. Co ja robię źle?

Wiem, że angular ma specjalne znaczenie dla tagu script. Czy mogę to zmienić?

Ranjith Ramachandra
źródło

Odpowiedzi:

101

Zaakceptowana odpowiedź nie będzie działać od wersji 1.2.0-rc1 + ( problem Github ).

Oto szybka poprawka stworzona przez endoramę :

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

Po prostu dodaj ten plik, załaduj ngLoadScriptmoduł jako zależność aplikacji i użyj type="text/javascript-lazy"jako typu dla skryptu, który ma być ładowany leniwie w częściach:

<script type="text/javascript-lazy">
  console.log("It works!");
</script>
Paolo Moretti
źródło
5
Nie sądzę, żeby to działało, jeśli twój tag skryptu ma atrybut src, a nie wbudowany js.
Blaskovicz
9
@Blaskovicz Wypróbuj ten: gist.github.com/subudeepak/9617483#file-angular-loadscript-js
Paolo Moretti
1
Działa to doskonale, ale jest jeszcze jedna rzecz, której chcę. Jak mogę to usunąć po zniszczeniu dyrektywy?
SLearner
Kiedy deklaruję kontroler w leniwym skrypcie, wystąpił błąd podczas odwoływania się do kontrolera w widoku. Wiesz dlaczego?
Changwang Zhang
To załatwiło sprawę. Użyłem go do uruchomienia kodu inicjalizacji karty dla zestawu kart załadowanego w części
LKallipo
39

Krótka odpowiedź: AngularJS ("jqlite") tego nie obsługuje. Dołącz jQuery do swojej strony (przed dołączeniem Angulara) i powinno działać. Zobacz https://groups.google.com/d/topic/angular/H4haaMePJU0/discussion

Mark Rajcok
źródło
2
Jak to działa, jeśli plik Partial.js zawiera kontroler? Szablon zostanie wyrenderowany przed załadowaniem kontrolera i wyświetli błąd: Argument „MyController” nie jest funkcją, został niezdefiniowany.
sthomps
2
@sthomps, zobacz, czy to pomoże: Dynamiczne ładowanie kontrolera
Mark Rajcok
16
Nie rozumiem, dlaczego to działa - czy przeglądarka nie załaduje automatycznie wprowadzonego tagu skryptu? Dlaczego jest to problem jQuery lub jqLite?
Scott Coates
u mnie też zadziałało, zawartość mojego tagu <script> nie była związana z angularjs. Kiedy nie ładowałem jQuery przed angularjs, wszystko w mojej części poniżej, łącznie ze znacznikiem script, zostało usunięte, bardzo dziwne.
Nick Russler
20

Wypróbowałem podejście neemzy, ale nie zadziałało przy używaniu 1.2.0-rc.3. Tag skryptu zostałby wstawiony do DOM, ale ścieżka javascript nie została załadowana. Podejrzewam, że to dlatego, że javascript, który próbowałem załadować, pochodzi z innej domeny / protokołu. Więc przyjąłem inne podejście i oto, co wymyśliłem, używając map Google jako przykładu: ( 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');
                    });
                }
            }
        };
    }]);

Mam nadzieję, że komuś to pomoże.

Neil S.
źródło
4
Dziwne, stworzyłem własną wersję na 1.2.0-rc3 z zewnętrznymi skryptami :) Twoja wersja i tak jest o wiele lepsza, dzięki za udostępnienie!
neemzy
12

Użyłem tej metody do dynamicznego ładowania pliku skryptu (wewnątrz kontrolera).

var script = document.createElement('script');
script.type = 'text/javascript';
script.src = "https://maps.googleapis.com/maps/api/js";
document.body.appendChild(script);
Azmeer
źródło
nice one @azmeer
Ali
Zagłosowano, ponieważ jest to proste i najlepsze rozwiązanie do dynamicznego ładowania skryptów.
RLD
8

To już nie zadziała od wersji 1.2.0-rc1. Zobacz ten problem, aby uzyskać więcej informacji na ten temat, w którym zamieściłem komentarz opisujący szybkie obejście. Podzielę się tym również tutaj:

// 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);
    }
}]);

Nie jest to najlepsze rozwiązanie w historii, ale hej, też nie jest umieszczanie tagów skryptów w kolejnych widokach. W moim przypadku muszę to zrobić, aby skorzystać z Facebooka / Twittera / itp. widżety.

neemzy
źródło
4

ocLazyLoad pozwala na leniwe ładowanie skryptów w szablonach / widokach przez routery (np. ui-router). Oto sniplet

$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();
        }]
    }
});  
Neil
źródło
stanąłem przed tym samym problemem. w aplikacji korporacyjnej musisz używać wszystkich tras, więc lepiej jest używać ng-include z zakładkami, ale ng-include ma swoje problemy, polegające na tym, że nie ma funkcji ładowania zależności.
Serak Shiferaw
1

Aby dynamicznie załadować recaptcha z a ui-viewużywam następującej metody:

W 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));


            }
        }
    };
});

W myPartial.client.view.html:

 <script type="application/javascript" ng-src="http://www.google.com/recaptcha/api.js?render=explicit&onload=vcRecaptchaApiLoaded"></script>
Michael Draper
źródło
czy to dodaje lub zastępuje dyrektywę angular native script? I czy to jest używanie do tego jquery?
jamie
1
Dodaje do. I nie, nie używa się tam Jquery
Michael Draper
thx - „nie używa się tutaj jQuery” -weird- $(element)dlaczego potrzebujesz $()składni. z dokumentu ajs docs „Wszystkie odwołania do elementów w Angular są zawsze opakowane w jQuery lub jqLite” (np. argument elementu w funkcji kompilacji / link dyrektywy) - więc z tego i innych moich doświadczeń - spodziewałem się, że użyjesz elementu „. append (...) `- chociaż ostatecznie nie byłem tym, czego mogłem użyć - podczas testowania byłem zdezorientowany, gdy zobaczyłem tę część i nie działał dla mnie, gdy próbowałem pracować tylko z 'element.append' składnia
jamie
nie tak, ale możesz to zastąpić angular.element (), jeśli chcesz, najprawdopodobniej.
Michael Draper
0

Niestety wszystkie odpowiedzi w tym poście nie działają dla mnie. Ciągle otrzymywałem następujący błąd.

Nie udało się wykonać „zapisu” w „dokumencie”: nie można pisać do dokumentu z asynchronicznie załadowanego skryptu zewnętrznego, chyba że zostanie on jawnie otwarty.

Dowiedziałem się, że dzieje się tak, gdy używasz widżetów innych firm (w moim przypadku popyt), które również wywołują dodatkowe zewnętrzne pliki JavaScript i próbują wstawić HTML. Patrząc na konsolę i kod JavaScript, zauważyłem wiele linii, takich jak to:

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

Użyłem plików JavaScript innych firm (htmlParser.js i postscribe.js) z: https://github.com/krux/postscribe . To rozwiązało problem w tym poście i jednocześnie naprawiło powyższy błąd.

(To była szybka i nieprzyjemna droga w napiętym terminie, który mam teraz. Nie czuję się jednak komfortowo w korzystaniu z biblioteki JavaScript innej firmy. Mam nadzieję, że ktoś może wymyślić czystszy i lepszy sposób).

Chris
źródło
0

Próbowałem użyć Google reCAPTCHA jawnie. Oto przykład:

// 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));
}
Romanyu
źródło