AngularJS:如何在ng-include内制作角度加载脚本?


85

嘿,我正在建立一个有角度的网页。问题是已经有一些东西没有角度了,我也必须包括它们

问题是这样的。

我的main.html中有这样的内容:

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

我的partial.html有这样的东西

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

我的partial.js与angularjs无关。nginclude工作正常,我可以看到html,但根本看不到正在加载的javascript文件。我知道如何使用firebug / chrome-dev-tool,但是我什至看不到正在发出的网络请求。我究竟做错了什么?

我知道angular对脚本标签有一些特殊的含义。我可以覆盖它吗?

Answers:


101

从1.2.0-rc1 +(Github问题)开始,可接受的答案将不起作用。

这是由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));

只需添加以下文件,将ngLoadScript模块作为应用程序依赖项加载,并type="text/javascript-lazy"作为您懒惰地局部加载脚本的脚本的类型:

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

5
我认为如果您的脚本标签具有src属性而不是内联js,这是行不通的。
Blaskovicz 2014年


1
这很完美,但是我还想做一件事。指令被销毁后如何删除?
SLearner 2014年

当我在惰性脚本中声明控制器时,在视图中引用控制器时出现错误。你知道为什么吗?
张长旺2015年

做到了。我用它来为部分加载的选项卡集运行选项卡初始化代码
LKallipo

39

简短答案:AngularJS(“ jqlite”)不支持此功能。在页面上包含jQuery(包括Angular之前),它应该可以工作。参见https://groups.google.com/d/topic/angular/H4haaMePJU0/discussion


2
如果您的partial.js包含控制器,该如何工作?该模板将在控制器加载之前呈现并给出错误:参数'MyController'不是一个函数,未定义。
sthomps

2
@sthomps,看看是否有帮助:动态加载控制器
Mark Rajcok

16
我不明白为什么这样做-浏览器不会自动加载注入的脚本标签吗?为什么这是jQuery或jqLit​​e问题?
Scott Coates 2013年

我的<script>标记的内容也与我无关。当我没有在angularjs之前加载jQuery时,我下面的部分以及包括script标签在内的所有内容都被删除了,这很奇怪。
Nick Russler

20

我尝试了neemzy的方法,但是使用1.2.0-rc.3。不适用于我。脚本标记将插入到DOM中,但不会加载javascript路径。我怀疑这是因为我尝试加载的JavaScript来自其他域/协议。因此,我采取了另一种方法,这就是我想出的方法,以谷歌地图为例:(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');
                    });
                }
            }
        };
    }]);

我希望这对某人有帮助。


4
很奇怪,我在1.2.0-rc3上用外部脚本制作了自己的版本:)无论如何,您的版本要好得多,谢谢分享!
neemzy 2013年

12

我使用这种方法来动态加载脚本文件(在控制器内部)。

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

好人@azmeer
阿里(Ali)

投票是因为它是动态脚本加载的简单且最佳的解决方案。
RLD

8

从1.2.0-rc1开始将不再起作用。有关此问题的更多信息,请参见此问题。我在其中发表了描述快速解决方案的评论。我也会在这里分享:

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

从来都不是最好的解决方案,但是,嘿,也没有将脚本标签放在后续视图中。就我而言,我必须这样做是为了使用Facebook / Twitter / etc。小部件。


4

ocLazyLoad允许通过路由器(例如ui-router)延迟在模板/视图中加载脚本。这是一个片段

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

我面临着同样的问题。在企业的应用程序,你必须使用所有的路由,以便其更好地使用NG-包括与标签,但NG-包括有它的问题是至极它没有依赖加载功能..
Serak Shiferaw

1

要从A动态加载Recaptcha,请ui-view使用以下方法:

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


            }
        }
    };
});

myPartial.client.view.html

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

会增加还是覆盖有角度的本机脚本指令?那是使用jQuery吗?
杰米

1
它增加了。不,那里没有使用Jquery
Michael Draper

thx-'此处不使用jQuery'-很奇怪-$(element)为什么需要$()语法。从ajs docs中开始:“ Angular中的所有元素引用总是包裹着jQuery或jqLit​​e”(例如指令的compile / link函数中的element参数) -因此,从我以及其他经验中-我希望您使用'element。 append(...)`-尽管最终我不是最终可以使用的东西-当进行测试时,我看到该部分时感到困惑,而当我尝试仅使用'element.append'时对我不起作用语法
杰米

是的,但是您很可能会用angular.element()替换它。
Michael Draper

0

不幸的是,这篇文章中的所有答案都不适用于我。我一直收到以下错误。

无法在“文档”上执行“写入”:无法通过异步加载的外部脚本写入文档,除非已明确打开该文档。

我发现,如果您使用一些第三方小部件(在我的情况下为demandforce)也发生了这种情况,这些小部件还调用了其他外部JavaScript文件并尝试插入HTML。查看控制台和JavaScript代码,我注意到这样的多行代码:

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

我使用了来自以下网址的第三方JavaScript文件(htmlParser.js和postscribe.js):https : //github.com/krux/postscribe。那解决了这篇文章中的问题,并同时修复了上述错误。

(在我现在紧迫的最后期限内,这是一种快速而肮脏的方法。但是,我对使用第三方JavaScript库不满意。我希望有人能提出一种更干净,更好的方法。)


0

我尝试明确使用Google reCAPTCHA。这是示例:

// 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));
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.