AngularJS: come creare uno script di carico angolare all'interno di ng-include?


85

Ehi, sto costruendo una pagina web con angolare. Il problema è che ci sono già delle cose costruite senza angolare e devo includerle anche io

Il problema è questo.

Ho qualcosa di simile nel mio main.html:

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

E il mio partial.html ha qualcosa del genere

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

E il mio partial.js non ha nulla a che fare con angularjs. nginclude funziona e posso vedere l'html, ma non riesco a vedere il file javascript caricato. So come usare firebug / chrome-dev-tool, ma non riesco nemmeno a vedere la richiesta di rete in corso. Che cosa sto facendo di sbagliato?

Penso che angolare abbia un significato speciale per il tag di script. Posso sovrascriverlo?

Risposte:


101

La risposta accettata non funzionerà da 1.2.0-rc1 + ( problema Github ).

Ecco una soluzione rapida creata da 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));

Aggiungi semplicemente questo file, carica il ngLoadScriptmodulo come dipendenza dell'applicazione e usa type="text/javascript-lazy"come tipo per lo script che caricare pigramente in parti parziali:

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

5
Non penso che funzioni se il tuo tag script ha un attributo src piuttosto che js inline.
Blaskovicz


1
Funziona perfettamente, tuttavia c'è ancora una cosa che voglio. Come lo rimuovo una volta che la direttiva è stata distrutta?
SLearner

Quando dichiaro controller nello script pigro, ho ricevuto un errore quando mi riferisco al controller nella vista. Sai perché?
Changwang Zhang,

Quello ha funzionato. L'ho usato per eseguire il codice di inizializzazione delle schede per un
schede

39

Risposta breve: AngularJS ("jqlite") non lo supporta. Includi jQuery nella tua pagina (prima di includere Angular) e dovrebbe funzionare. Vedi https://groups.google.com/d/topic/angular/H4haaMePJU0/discussion


2
Come funziona se il tuo partial.js contiene un controller? Il modello verrà visualizzato prima del caricamento del controller e restituirà un errore: L'argomento "MyController" non è una funzione, non è stato definito.
sthomps

2
@sthomps, vedi se questo aiuta: Caricamento dinamico di un controller
Mark Rajcok

16
Non capisco perché funziona: il browser non caricherà automaticamente un tag di script inserito? Perché questo è un problema di jQuery o jqLite?
Scott Coates

ha funzionato anche per me, il contenuto del mio tag <script> era angularjs non correlato. Quando non ho caricato jQuery prima di angularjs, tutto nel mio parziale sotto e incluso il tag script è stato rimosso, molto strano.
Nick Russler

20

Ho provato l'approccio di neemzy, ma non ha funzionato per me usando 1.2.0-rc.3. Il tag script verrebbe inserito nel DOM, ma il percorso javascript non verrebbe caricato. Sospetto che fosse perché il javascript che stavo cercando di caricare proveniva da un dominio / protocollo diverso. Così ho preso un approccio diverso, e questo è ciò che mi è venuta, utilizzando Google Maps come un esempio: ( 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');
                    });
                }
            }
        };
    }]);

Spero sia utile per qualcuno.


4
Strano, ho creato la mia versione su 1.2.0-rc3 con script esterni :) La tua versione è comunque molto migliore, grazie per averla condivisa!
neemzy

12

Ho usato questo metodo per caricare dinamicamente un file di script (all'interno di un controller).

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

simpatico @azmeer
Ali

Votato perché è la soluzione semplice e migliore per il caricamento dinamico degli script.
RLD

8

Questo non funzionerà più da 1.2.0-rc1. Vedi questo problema per ulteriori informazioni al riguardo, in cui ho pubblicato un commento che descrive una rapida soluzione. Lo condividerò anche qui:

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

Non è la soluzione migliore in assoluto, ma ehi, nemmeno l'inserimento di tag di script nelle visualizzazioni successive. Nel mio caso devo farlo per poter utilizzare Facebook / Twitter / ecc. widget.


4

ocLazyLoad consente di caricare pigramente gli script nei modelli / viste tramite router (ad esempio ui-router). Ecco uno 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();
        }]
    }
});  

ho affrontato lo stesso problema come questo. nell'app aziendale devi usare tutti i tuoi percorsi quindi è meglio usare ng-include con con le schede ma ng-include ha i suoi problemi che non ha la funzione di caricamento delle dipendenze ..
Serak Shiferaw

1

Per caricare dinamicamente recaptcha da un ui-viewutilizzo il seguente metodo:

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


            }
        }
    };
});

In myPartial.client.view.html:

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

questo aggiunge o sovrascrive la direttiva angular native script? E sta usando jquery con quello?
Jamie

1
Si aggiunge a. E no, non viene utilizzato alcun Jquery lì
Michael Draper

thx - 'nessun jQuery è usato qui' -weird- $(element)perché hai bisogno della $()sintassi. dai documenti ajs 'Tutti i riferimenti agli elementi in Angular sono sempre racchiusi con jQuery o jqLite' (come l'argomento dell'elemento nella funzione di compilazione / collegamento di una direttiva) - quindi da quella e da altre mie esperienze - mi aspettavo che usassi l'elemento '. append (...) `- anche se alla fine non sono stato quello che potevo usare - durante il test sono rimasto confuso quando ho visto quella parte e non ha funzionato per me quando ho provato a lavorare solo con 'element.append' sintassi
jamie

doh sì lo è, ma potresti sostituirlo con angular.element () se lo desideri, molto probabilmente.
Michael Draper

0

Sfortunatamente tutte le risposte in questo post non hanno funzionato per me. Continuavo a ricevere il seguente errore.

Impossibile eseguire la "scrittura" su "Documento": non è possibile scrivere in un documento da uno script esterno caricato in modo asincrono a meno che non sia aperto in modo esplicito.

Ho scoperto che ciò accade se utilizzi alcuni widget di terze parti (nel mio caso demandforce) che chiamano anche file JavaScript esterni aggiuntivi e provi a inserire HTML. Guardando la console e il codice JavaScript, ho notato più righe come questa:

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

Ho utilizzato file JavaScript di terze parti (htmlParser.js e postscribe.js) da: https://github.com/krux/postscribe . Ciò ha risolto il problema in questo post e allo stesso tempo risolto l'errore precedente.

(Questo è stato un modo rapido e sporco per aggirare la stretta scadenza che ho ora. Non mi sento a mio agio con l'utilizzo della libreria JavaScript di terze parti. Spero che qualcuno possa trovare un modo migliore e più pulito.)


0

Ho provato a utilizzare Google reCAPTCHA in modo esplicito. Ecco l'esempio:

// 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));
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.