使用jQuery加载Angular会导致调用脚本onload回调失败



我想动态加载一个JavaScript库,比如说D3.js,使用一个类似于以下的Angular服务:

function factoryD3Loading ($q, $document, $rootScope, $window) {
    var d = $q.defer();
    var script = $document[0].createElement('script');
    script.type = 'text/javascript';
    script.async = 'async';
    script.src = 'https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js';
    script.onload = function () {
        $rootScope.$apply(function () {
            d.resolve($window.d3);
        });
    };
    $document.find('body').append(script);
    return d.promise;
}

它运行得很好,除非我用jQuery加载Angular,而不是默认情况下让它使用内置的jqLite。据我所知,当Angular使用jQuery时,onload回调根本不会被调用。当Angular只是使用jqLite时,它肯定会被调用。

这里有一个使用jQuery的不工作的fiddle,还有一个没有jQuery的工作fiddle。请注意,除了是否加载jQuery之外,这些都是相同的。

因此,一个由两部分组成的问题:

  1. 为什么使用jQuery会导致这种行为差异

  2. 如何纠正或解决jQuery行为

至于为什么,我的猜测是,jqLite以某种方式配置了摘要循环,而Angular没有(或者不能?)为jQuery配置。如果这个猜测是准确的,那么这种行为似乎是不一致的,奇怪到了成为bug的地步(也就是说,在Angular中)。

至于如何,我认为(但尚未测试)我可以在Angular之后加载jQuery,并在需要时进行一些丑陋的解包/重写以"转换"jqLite对象,使其拥有jQuery的完整API。如果存在更干净的方法,我更喜欢这种方法。

这不是Angular中的bug。。。之所以会发生这种情况,是因为jQuery.append以一种特殊的方式处理脚本元素,基本上是通过jQueryAjax单独加载脚本元素上的任何src URL。然后在window上而不是在脚本元素上激发load。jqLite.append没有模仿jQuery.append.的这一方面

解决方案是使用DOM而不是angular进行附加。element:

function factoryD3Loading ($q, $document, $rootScope, $window) {
    var d = $q.defer();
    var script = $document[0].createElement('script');
    script.type = 'text/javascript';
    script.async = 'async';
    script.src = 'https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js';
    script.onload = function () {
        $rootScope.$apply(function () {
            d.resolve($window.d3);
        });
    };
    $document[0].body.appendChild(script); // DOM, not angular.element
    return d.promise;
}

通过此更改,jqLite Angular和jQuery Angular之间的行为是一致的。

最新更新