AngularJS: Como fazer script de carregamento angular dentro do ng-include?

85

Ei, estou construindo uma página da web com angular. O problema é que já existem coisas construídas sem angular e tenho que incluí-las também

O problema é este.

Tenho algo assim no meu main.html:

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

E meu partial.html tem algo assim

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

E meu partial.js não tem nada a ver com angularjs. nginclude funciona e posso ver o html, mas não consigo ver o arquivo javascript sendo carregado. Eu sei como usar o firebug / chrome-dev-tool, mas não consigo nem ver a solicitação de rede sendo feita. O que estou fazendo errado?

Eu sei que angular tem algum significado especial para tag de script. Posso substituí-lo?

Ranjith Ramachandra
fonte

Respostas:

101

A resposta aceita não funcionará a partir de 1.2.0-rc1 + ( problema do Github ).

Aqui está uma solução rápida criada 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));

Basta adicionar este arquivo, carregar o ngLoadScriptmódulo como dependência do aplicativo e usar type="text/javascript-lazy"como tipo de script para carregar lentamente em partes:

<script type="text/javascript-lazy">
  console.log("It works!");
</script>
Paolo Moretti
fonte
5
Não acho que isso funcione se sua tag de script tiver um atributo src em vez de js embutido.
Blaskovicz,
9
@Blaskovicz Experimente este aqui: gist.github.com/subudeepak/9617483#file-angular-loadscript-js
Paolo Moretti
1
Isso funciona perfeitamente, mas há mais uma coisa que eu quero. Como faço para remover isso depois que a diretiva for destruída?
SLearner de
Quando declaro o controlador no script preguiçoso, recebo um erro ao referir-me ao controlador na visualização. Você sabe por quê?
Changwang Zhang
Isso funcionou. Usei-o para executar o código de inicialização da guia para um conjunto de guias carregado no parcial
LKallipo
39

Resposta curta: AngularJS ("jqlite") não suporta isso. Inclua jQuery em sua página (antes de incluir Angular) e deve funcionar. Consulte https://groups.google.com/d/topic/angular/H4haaMePJU0/discussion

Mark Rajcok
fonte
2
Como isso funciona se seu partial.js contém um controlador? O template irá renderizar antes de o controlador carregar e dar um erro: O argumento 'MyController' não é uma função, foi indefinido.
sthomps
2
@sthomps, veja se isso ajuda: Carregando um controlador dinamicamente
Mark Rajcok
16
Não entendo por que isso funciona - o navegador não carrega automaticamente uma tag de script injetada? Por que isso é uma preocupação jQuery ou jqLite?
Scott Coates de
funcionou para mim também, o conteúdo da minha tag <script> era angularjs não relacionado. Quando eu não carreguei o jQuery antes do angularjs, tudo no meu parcial abaixo e incluindo a tag do script foi removido, muito estranho.
Nick Russler
20

Eu tentei a abordagem do Neemzy, mas não funcionou para mim usando 1.2.0-rc.3. A tag de script seria inserida no DOM, mas o caminho javascript não seria carregado. Suspeito que seja porque o javascript que eu estava tentando carregar era de um domínio / protocolo diferente. Então eu peguei uma abordagem diferente, e é isso que eu inventei, usando o google maps como exemplo: ( Síntese )

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 seja útil para alguém.

Neil S
fonte
4
Estranho, fiz minha própria versão em 1.2.0-rc3 com scripts externos :) Sua versão é muito melhor de qualquer maneira, obrigado por compartilhar isso!
neemzy
12

Usei esse método para carregar um arquivo de script dinamicamente (dentro de um controlador).

var script = document.createElement('script');
script.type = 'text/javascript';
script.src = "https://maps.googleapis.com/maps/api/js";
document.body.appendChild(script);
Azmeer
fonte
legal @azmeer
Ali
Votado por ser simples e a melhor solução para carregamento dinâmico de scripts.
RLD de
8

Isso não funcionará mais a partir de 1.2.0-rc1. Consulte este problema para obter mais informações, no qual postei um comentário descrevendo uma solução alternativa rápida. Vou compartilhar aqui também:

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

Não é a melhor solução de todas, mas ei, nem é colocar tags de script em visualizações subsequentes. No meu caso, tenho que fazer isso para usar o Facebook / Twitter / etc. widgets.

neemzy
fonte
4

ocLazyLoad permite carregar scripts de forma preguiçosa nos modelos / visualizações por meio de roteadores (por exemplo, ui-router). Aqui está um 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
fonte
eu enfrentei o mesmo problema como este. no aplicativo corporativo, você deve usar todas as suas rotas, então é melhor usar o ng-include com as guias, mas o ng-include tem seus problemas, pois não tem recurso de carregamento de dependência.
Serak Shiferaw
1

Para carregar dinamicamente o recaptcha de um ui-view, uso o seguinte método:

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


            }
        }
    };
});

Em myPartial.client.view.html:

 <script type="application/javascript" ng-src="http://www.google.com/recaptcha/api.js?render=explicit&onload=vcRecaptchaApiLoaded"></script>
Michael Draper
fonte
isso adiciona ou substitui a diretiva de script nativo angular? E isso é usar jquery com isso?
Jamie
1
Acrescenta a. E não, nenhum Jquery é usado lá
Michael Draper
thx - 'nenhum jQuery é usado aqui' - estranho - $(element)por que você precisa da $()sintaxe. da documentação ajs 'Todas as referências de elemento em Angular são sempre agrupadas com jQuery ou jqLite' (como o argumento de elemento na função de compilação / link de uma diretiva) - portanto, a partir dessa e de minhas outras experiências - esperava que você usasse o elemento '. append (...) `- embora eu acabe não sendo o que eu poderia usar no final - ao testar eu fiquei confuso quando vi essa parte e não funcionou para mim quando tentei funcionar apenas com o 'element.append' sintaxe
jamie
doh sim é, mas você poderia substituir isso por angular.element () se você quiser, provavelmente.
Michael Draper
0

Infelizmente, todas as respostas neste post não funcionaram para mim. Eu continuei recebendo o seguinte erro.

Falha ao executar 'escrever' em 'Documento': não é possível escrever em um documento a partir de um script externo carregado de forma assíncrona, a menos que seja explicitamente aberto.

Descobri que isso acontece se você usar alguns widgets de terceiros (demandforce no meu caso) que também chamam arquivos JavaScript externos adicionais e tenta inserir HTML. Olhando para o console e o código JavaScript, percebi várias linhas como esta:

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

Usei arquivos JavaScript de terceiros (htmlParser.js e postscribe.js) de: https://github.com/krux/postscribe . Isso resolveu o problema neste post e corrigiu o erro acima ao mesmo tempo.

(Esta foi uma maneira rápida e suja dentro do prazo apertado que tenho agora. Não me sinto confortável em usar a biblioteca JavaScript de terceiros. Espero que alguém possa encontrar uma maneira mais limpa e melhor.)

Chris
fonte
0

Tentei usar o reCAPTCHA do Google explicitamente. Aqui está o exemplo:

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