AngularJS: ¿Cómo hacer un script de carga angular dentro de ng-include?

85

Hola, estoy construyendo una página web con angular. El problema es que ya hay cosas construidas sin angular y tengo que incluirlas también

El problema es este.

Tengo algo como esto en mi main.html:

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

Y mi parcial.html tiene algo como esto

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

Y mi parcial.js no tiene nada que ver con angularjs. nginclude funciona y puedo ver el html, pero no puedo ver el archivo javascript que se carga en absoluto. Sé cómo usar firebug / chrome-dev-tool, pero ni siquiera puedo ver la solicitud de red que se está realizando. ¿Qué estoy haciendo mal?

Yo knwo angular tiene un significado especial para la etiqueta de script. ¿Puedo anularlo?

Ranjith Ramachandra
fuente

Respuestas:

101

La respuesta aceptada no funcionará desde 1.2.0-rc1 + ( problema de Github ).

Aquí hay una solución rápida creada por 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));

Simplemente agregue este archivo, cargue el ngLoadScriptmódulo como dependencia de la aplicación y úselo type="text/javascript-lazy"como tipo para el script que cargará perezosamente en parciales:

<script type="text/javascript-lazy">
  console.log("It works!");
</script>
Paolo Moretti
fuente
5
No creo que esto funcione si su etiqueta de script tiene un atributo src en lugar de js en línea.
Blaskovicz
9
@Blaskovicz Prueba este: gist.github.com/subudeepak/9617483#file-angular-loadscript-js
Paolo Moretti
1
Esto funciona perfectamente, sin embargo, hay una cosa más que quiero. ¿Cómo elimino esto una vez que se ha destruido la directiva?
SLearner
Cuando declaro el controlador en el script perezoso, obtengo un error cuando me refiero al controlador en la vista. ¿Sabes por qué?
Changwang Zhang
Eso hizo el truco. Lo he usado para ejecutar el código de inicialización de pestañas para un conjunto de pestañas cargado en el parcial
LKallipo
39

Respuesta corta: AngularJS ("jqlite") no admite esto. Incluya jQuery en su página (antes de incluir Angular), y debería funcionar. Ver https://groups.google.com/d/topic/angular/H4haaMePJU0/discussion

Mark Rajcok
fuente
2
¿Cómo funciona esto si su archivo .js parcial contiene un controlador? La plantilla se renderizará antes de que se cargue el controlador y dará un error: El argumento 'MyController' no es una función, no está definido.
sthomps
2
@sthomps, vea si esto ayuda: Cargar un controlador dinámicamente
Mark Rajcok
16
No entiendo por qué funciona esto: ¿el navegador no cargará automáticamente una etiqueta de secuencia de comandos inyectada? ¿Por qué es esto una preocupación de jQuery o jqLite?
Scott Coates
también funcionó para mí, el contenido de mi etiqueta <script> no estaba relacionado con angularjs. Cuando no cargué jQuery antes de angularjs, se eliminó todo en mi parcial a continuación, incluida la etiqueta de script, muy extraño.
Nick Russler
20

Probé el enfoque de neemzy, pero no funcionó para mí usando 1.2.0-rc.3. La etiqueta de secuencia de comandos se insertaría en el DOM, pero la ruta de JavaScript no se cargaría. Sospecho que fue porque el javascript que estaba tratando de cargar era de un dominio / protocolo diferente. Así que tomé un enfoque diferente, y esto es lo que se me ocurrió, usando Google Maps como ejemplo: ( 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');
                    });
                }
            }
        };
    }]);

Espero que sea de ayuda para alguien.

Neil S
fuente
4
Extraño, hice mi propia versión en 1.2.0-rc3 con scripts externos :) Tu versión es mucho mejor de todos modos, ¡gracias por compartir esto!
neemzy
12

Usé este método para cargar un archivo de script dinámicamente (dentro de un controlador).

var script = document.createElement('script');
script.type = 'text/javascript';
script.src = "https://maps.googleapis.com/maps/api/js";
document.body.appendChild(script);
Azmeer
fuente
nice one @azmeer
Ali
Votado porque es la mejor solución para la carga dinámica de scripts.
RLD
8

Esto ya no funcionará desde 1.2.0-rc1. Consulte este problema para obtener más información al respecto, en el que publiqué un comentario que describe una solución rápida. Lo compartiré aquí también:

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

No es la mejor solución, pero bueno, tampoco lo es poner etiquetas de script en vistas posteriores. En mi caso, tengo que hacer esto para poder usar Facebook / Twitter / etc. widgets.

neemzy
fuente
4

ocLazyLoad permite cargar scripts de forma perezosa en las plantillas / vistas a través de enrutadores (por ejemplo, ui-enrutador). Aquí hay un fragmento

$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
fuente
Me enfrenté al mismo problema como este. en la aplicación empresarial, debe usar todas sus rutas, por lo que es mejor usar ng-include con pestañas, pero ng-include tiene sus problemas, es decir, no tiene función de carga de dependencia ..
Serak Shiferaw
1

Para cargar dinámicamente recaptcha desde un ui-view, utilizo el siguiente método:

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


            }
        }
    };
});

En myPartial.client.view.html:

 <script type="application/javascript" ng-src="http://www.google.com/recaptcha/api.js?render=explicit&onload=vcRecaptchaApiLoaded"></script>
Michael Draper
fuente
¿Eso se suma o anula la directiva de script nativo angular? ¿Y eso es usar jquery con eso?
Jamie
1
Se suma a. Y no, no se usa Jquery allí
Michael Draper
thx - 'no se usa jQuery aquí' -extraño- $(element)¿por qué necesita la $()sintaxis? de los documentos de ajs 'Todas las referencias de elementos en Angular siempre están envueltas con jQuery o jqLite' (como el argumento del elemento en la función de compilación / enlace de una directiva) , por lo que a partir de eso y mis otras experiencias, esperaba que usaras 'element. append (...) `- aunque terminé no siendo lo que podía usar al final - cuando probé me confundí cuando vi esa parte y no funcionó para mí cuando intenté trabajar solo con el 'element.append' sintaxis
jamie
doh sí lo es, pero podría reemplazarlo con angular.element () si quisiera, lo más probable.
Michael Draper
0

Desafortunadamente, todas las respuestas de esta publicación no me funcionaron. Seguí recibiendo el siguiente error.

Error al ejecutar 'escribir' en 'Documento': no ​​es posible escribir en un documento desde un script externo cargado de forma asincrónica a menos que se abra explícitamente.

Descubrí que esto sucede si usa algunos widgets de terceros (demandforce en mi caso) que también llaman a archivos JavaScript externos adicionales e intentan insertar HTML. Mirando la consola y el código JavaScript, noté varias líneas como esta:

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

Usé archivos JavaScript de terceros (htmlParser.js y postscribe.js) de: https://github.com/krux/postscribe . Eso resolvió el problema en esta publicación y corrigió el error anterior al mismo tiempo.

(Esta fue una forma rápida y sucia en el plazo ajustado que tengo ahora. Sin embargo, no me siento cómodo con el uso de la biblioteca de JavaScript de terceros. Espero que alguien pueda encontrar una forma más limpia y mejor).

Chris
fuente
0

Intenté usar Google reCAPTCHA explícitamente. Aquí está el ejemplo:

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