AngularJS: Comment créer un script de chargement angulaire dans ng-include?

85

Hé, je construis une page Web avec angulaire. Le problème est qu'il y a déjà des éléments construits sans angulaire et je dois aussi les inclure

Le problème est le suivant.

J'ai quelque chose comme ça dans mon main.html:

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

Et mon partial.html a quelque chose comme ça

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

Et mon partial.js n'a rien à voir avec angularjs. nginclude fonctionne et je peux voir le html, mais je ne vois pas du tout le fichier javascript en cours de chargement. Je sais comment utiliser firebug / chrome-dev-tool, mais je ne peux même pas voir la requête réseau en cours. Qu'est-ce que je fais mal?

Je sais que angular a une signification particulière pour la balise de script. Puis-je le remplacer?

Ranjith Ramachandra
la source

Réponses:

101

La réponse acceptée ne fonctionnera pas à partir de 1.2.0-rc1 + ( problème Github ).

Voici une solution rapide créée par 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));

Ajoutez simplement ce fichier, chargez le ngLoadScriptmodule en tant que dépendance d'application et utilisez-le type="text/javascript-lazy"comme type de script à charger paresseusement en partiels:

<script type="text/javascript-lazy">
  console.log("It works!");
</script>
Paolo Moretti
la source
5
Je ne pense pas que cela fonctionne si votre balise de script a un attribut src plutôt que js en ligne.
Blaskovicz
9
@Blaskovicz Essayez celui-ci: gist.github.com/subudeepak/9617483#file-angular-loadscript-js
Paolo Moretti
1
Cela fonctionne parfaitement, mais il y a encore une chose que je veux. Comment le supprimer une fois la directive détruite?
SLearner
Lorsque je déclare le contrôleur dans le script paresseux, j'obtiens une erreur en me référant au contrôleur dans la vue. Est-ce que tu sais pourquoi?
Changwang Zhang
Cela a fait l'affaire. Je l'ai utilisé pour exécuter le code d'initialisation des onglets pour un ensemble de tabulations chargé dans le partiel
LKallipo
39

Réponse courte: AngularJS ("jqlite") ne le prend pas en charge. Incluez jQuery sur votre page (avant d'inclure Angular), et cela devrait fonctionner. Voir https://groups.google.com/d/topic/angular/H4haaMePJU0/discussion

Mark Rajcok
la source
2
Comment cela fonctionne-t-il si votre partial.js contient un contrôleur? Le modèle sera rendu avant le chargement du contrôleur et donnera une erreur: l'argument «MyController» n'est pas une fonction, il n'a pas été défini.
sthomps
2
@sthomps, voyez si cela aide: Charger un contrôleur dynamiquement
Mark Rajcok
16
Je ne comprends pas pourquoi cela fonctionne - le navigateur ne chargera-t-il pas automatiquement une balise de script injectée? Pourquoi est-ce un problème jQuery ou jqLite?
Scott Coates
a également fonctionné pour moi, le contenu de ma balise <script> était sans rapport avec angularjs. Quand je n'ai pas chargé jQuery avant angularjs, tout dans mon partiel ci-dessous et y compris la balise de script a été supprimé, très étrange.
Nick Russler
20

J'ai essayé l'approche de neemzy, mais cela n'a pas fonctionné pour moi en utilisant 1.2.0-rc.3. La balise script serait insérée dans le DOM, mais le chemin javascript ne serait pas chargé. Je soupçonne que c'était parce que le javascript que j'essayais de charger provenait d'un domaine / protocole différent. J'ai donc adopté une approche différente, et c'est ce que j'ai proposé, en utilisant Google Maps comme exemple: ( 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');
                    });
                }
            }
        };
    }]);

J'espère que c'est utile pour quelqu'un.

Neil S
la source
4
Bizarre, j'ai fait ma propre version sur 1.2.0-rc3 avec des scripts externes :) Votre version est bien meilleure de toute façon, merci de partager ça!
neemzy
12

J'ai utilisé cette méthode pour charger un fichier de script dynamiquement (à l'intérieur d'un contrôleur).

var script = document.createElement('script');
script.type = 'text/javascript';
script.src = "https://maps.googleapis.com/maps/api/js";
document.body.appendChild(script);
Azmeer
la source
nice one @azmeer
Ali
Voté parce que c'est la meilleure solution simple pour le chargement dynamique de scripts.
RLD
8

Cela ne fonctionnera plus à partir de 1.2.0-rc1. Pour en savoir plus, consultez ce numéro , dans lequel j'ai publié un commentaire décrivant une solution de contournement rapide. Je vais également le partager ici:

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

Ce n'est pas la meilleure solution de tous les temps, mais bon, ni placer des balises de script dans les vues suivantes. Dans mon cas, je dois le faire pour utiliser Facebook / Twitter / etc. widgets.

neemzy
la source
4

ocLazyLoad permet de charger paresseusement des scripts dans les modèles / vues via des routeurs (par exemple ui-router). Voici un 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
la source
J'ai fait face au même problème comme celui-ci. dans l'application d'entreprise, vous devez utiliser toutes vos routes, il est donc préférable d'utiliser ng-include avec avec des onglets, mais ng-include a ses problèmes, c'est-à-dire qu'il n'a pas de fonction de chargement de dépendances ..
Serak Shiferaw
1

Pour charger dynamiquement recaptcha à partir d'un, ui-viewj'utilise la méthode suivante:

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


            }
        }
    };
});

Dans myPartial.client.view.html:

 <script type="application/javascript" ng-src="http://www.google.com/recaptcha/api.js?render=explicit&onload=vcRecaptchaApiLoaded"></script>
Michael Draper
la source
est-ce que cela ajoute ou remplace la directive de script natif angulaire? Et est-ce que cela utilise jquery avec ça?
jamie
1
Cela ajoute à. Et non, aucun Jquery n'est utilisé là
Michael Draper
thx - 'aucun jQuery n'est utilisé ici' -weird- $(element)pourquoi avez-vous besoin de la $()syntaxe. à partir de la documentation ajs 'Toutes les références d'élément dans Angular sont toujours enveloppées avec jQuery ou jqLite' (comme l'argument élément dans la fonction de compilation / liaison d'une directive) - donc à partir de cela et de mes autres expériences - je m'attendais à ce que vous utilisiez 'element. append (...) `- même si je n'ai finalement pas été ce que je pouvais utiliser à la fin - lors du test, j'étais confus quand j'ai vu cette partie et ne fonctionnait pas pour moi quand j'ai essayé de travailler uniquement avec l'élément 'element.append' syntaxe
jamie
oui oui, mais vous pouvez le remplacer par angular.element () si vous le souhaitez, très probablement.
Michael Draper
0

Malheureusement, toutes les réponses dans cet article n'ont pas fonctionné pour moi. J'ai continué à recevoir l'erreur suivante.

Impossible d'exécuter 'write' sur 'Document': il n'est pas possible d'écrire dans un document à partir d'un script externe chargé de manière asynchrone à moins qu'il ne soit ouvert explicitement.

J'ai découvert que cela se produit si vous utilisez des widgets tiers (demandforce dans mon cas) qui appellent également des fichiers JavaScript externes supplémentaires et essayez d'insérer du HTML. En regardant la console et le code JavaScript, j'ai remarqué plusieurs lignes comme celle-ci:

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

J'ai utilisé des fichiers JavaScript tiers (htmlParser.js et postscribe.js) à partir de: https://github.com/krux/postscribe . Cela a résolu le problème dans ce post et corrigé l'erreur ci-dessus en même temps.

(C'était un moyen rapide et sale de contourner le délai serré que j'ai maintenant. Je ne suis pas à l'aise avec l'utilisation d'une bibliothèque JavaScript tierce. J'espère que quelqu'un pourra trouver un moyen plus propre et meilleur.)

Chris
la source
0

J'ai essayé d'utiliser Google reCAPTCHA explicitement. Voici l'exemple:

// 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
la source