【问题标题】:Load dynamic script block with external widget in angularjs在angularjs中加载带有外部小部件的动态脚本块
【发布时间】:2015-10-20 12:35:31
【问题描述】:

我有脚本块可以在部分视图上加载小部件,但如果我直接放在部分 html 页面上,则会出现以下错误:

“在‘文档’上执行‘写入’失败:无法写入 从异步加载的外部脚本到文档中,除非 它是显式打开的。”

示例脚本块是:

> <script type="text/javascript"  defer="defer"
> src="http://svenskfotboll.se/widget.aspx?scr=table&amp;ftid=57108&amp;b1=%232f3841&amp;f1=%23ffffff&amp;b2=%23acbfda&amp;f2=%23000000&amp;b3=%23ffffff&amp;f3=%23000000&amp;b4=%23ececec&amp;bo=%23dfdfdf&amp;s=1"></script>

我将有多个小部件,所以我想通过 ng-repeat 加载一些东西。请指教。

注意 我试图为此显示静态方式,我添加了一个带有 html/body 标签的 html 页面,并将该脚本块放在里面,然后通过 iframe 加载该页面,然后它就可以工作了。但问题是在加载到 iframe 之前更新内容非常困难

【问题讨论】:

    标签: javascript html angularjs


    【解决方案1】:

    如果没有看到外部小部件的某些内容,很难准确诊断。我的第一印象是它可能需要更改以返回一个文档,然后您可以使用外部小部件中的标头函数的名称在 JS 脚本块中分配该文档。大致如下:

    var someDoc = writeDoc();
    

    writeDoc() 是小部件中的标题函数,我假设它返回某种 HTML 文档。当然,脚本标签必须在您的声明之前。或者,根据您的ng-repeat 要求,您可以使用类似:

    ng-repeat="{{writeDoc()}}"
    

    在列表块甚至选择块中(虽然我不推荐这样的东西),具体取决于文档占用多少空间。

    希望对您有所帮助。

    -C§

    【讨论】:

    • 已发布脚本块
    • 脚本块已经张贴在那里。是的,你是对的,当我将相同的脚本放在索引文件上时,它会正确加载。但是,当放在局部视图上时,就不是了。两个解决方法 1. 将脚本块放入 html 文件中,然后加载到 iframe 中。但问题是,这个脚本是用户群,所以我需要动态更改 html 文件。它不是可行的解决方案。 2.第二种解决方案是您建议需要修改内容,因此脚本块从url下载内容,然后删除页面上的document.write显示。但问题是他们不允许的 CROS 域请求。1
    • 您是在索引页面上还是在导致它的过程中进行认证?不允许听起来您需要加载虚拟凭据或让用户登录并将凭据保存到您的 index.html 中的调用中。
    • 我的要求很简单:支持 user1 有两个小部件,而 user2 有 3 个小部件,所以如果 user1 已登录,则在部分页面上加载两个小部件。
    【解决方案2】:
    1. 使用 $compile 服务编译您动态加载的 html 内容,同时将其绑定到作用域。
    2. 建议你做一个指令。您可以使用属性传入部分的 url。

    特别是指令的链接属性是这样的:

        link: function (scope, ele, attrs) {
          scope.$watch(attrs.dynamic, function(html) {
            ele.html(html);
            $compile(ele.contents())(scope);
          });
        }
    

    详见answer to this question,它提供了一个plunker demo。

    希望这会有所帮助。

    编辑 我想写更多来回应@Víťa Plšek - angular.cz 的回答。 我曾经做过更动态的事情,就是在partial中加载javacsript!

    曾几何时,我基于angularjs做了一个单页应用。每个菜单功能都是使用 ajax 动态加载的部分。当然,每个菜单功能都是一个单独的角度控制器。为了使代码更易于管理,我没有将所有控制器的代码合并到一个文件中,而是从 index.html 中一次性加载它们。相反,我让每个部分使用自定义指令指定其自己的控制器的 js 代码并进行延迟加载。

    所以单页应用程序的主页使用自定义指令来包含动态加载的部分:

    <body>
    ...
        <dynamic-partial src={{selected_partial_url}}>
        </dynamic-partial>
    ...
    </body>
    

    ,而每个部分都存储在一个单独的 html 文件中。它的内容是这样的:

    <lazy-load-script src="js/controllers/dynamic-controller1.js" >
    </lazy-load-script>
    
    <div ng-controller="DynamicController1">
        ...
    </div>
    

    现在,有一个问题:如果我们一次编译部分文件,会出现一个错误,说DynamicController1 没有定义。是真的。我们必须先加载dynamic-controller1.js,然后在js文件加载完成后编译&lt;div ng-controller="DynamicController1"&gt;&lt;/div&gt;部分。

    所以指令DynamicPartial的代码是这样的:

    "use strict";
    angular.module('MyApp')
        .directive('DynamicPartial', [ "$compile", function( $compile ) {
            return {
                restrict: 'E',
                scope: {
                    src: '@'
                },
                link: function (scope, element, attr) {
                    console.log( "Linking web-terminal. src = " + scope.src );
                    scope.onLazyLoadDone = function(){
                    // compile the other html elements in the partial, and put them in <dynamic-partial> element.
                        element.html( $compile( scope.other_elements_html )(scope) );
                    }
                    attr.$observe( 'src', function( value ){ // fetch new partial as soon as 'src' attribute changes:
                        console.log( "'src' attribute changed. Fetching new partial: " + value );
                        if( "" == scope.src ) {
                            element.addClass("ng-hide");
                            return;
                        }
                        else
                            element.removeClass("ng-hide");
                        $.ajax({
                                dataType: 'text',
                                timeout: 5000,
                                url: scope.src
                            })
                            .done(function (data) {
                                console.log( "Successfully fetched terminal file, length = " + data.length);
                                // compile the <lazy-load-script> tag first:
                                var lazy_load_element = $('<div/>').append( $(data)).find( "lazy-load-script, [lazy-load-script]" );
                                if( lazy_load_element.length > 0 ) { // lazy-load-script element found:
                                    // Here we pragmatically set the "on-loaded" attribute for the <lazy-load-script> element found in the loaded partial, so that we can get called when the javascript file specified by <lazy-load-script> element finishes loading.
                                    lazy_load_element.attr("on-loaded", "onLazyLoadDone()");
                                    $compile($('<div/>').append(lazy_load_element).html())(scope);
                                }
                                // Save the remaining DOM elements in the partial, which will be compiled after the lazy-load is done.
                                scope.other_elements_html = $('<div/>').append( $(data).filter(':not([lazy-load],lazy-load)')).html();
                                if( 0 == lazy_load_element.length )
                                    scope.onLazyLoadDone();
                            })
    
                            .fail(function (jqXHR, textStatus, errorThrown) {
                                console.error("Failed fetching menu from server: " + errorThrown.message);
                            });
                    });
                    if( undefined == scope.src  || 'not defined' == scope.src ) {
                        return;
                    }
                }
            };
        }]);
    

    &lt;lazy-load-script&gt;的代码如下。关键是使用onLoaded 范围属性来指定对父控制器的回调。

    "use strict";
    angular.module('SyncreonApp')
        .directive('lazyLoad', [ "$ocLazyLoad",  function( $ocLazyLoad ) {
            return {
                restrict: 'EA',
                scope: {
                    src: '@',
                    onLoaded: '&'
                },
                link: function (scope, element, attr) {
                    //console.log( "Linking lazyLoadScript, url to load = " + scope.src );
                    if( undefined != scope.src && '' != scope.src ){
                        $ocLazyLoad.load( scope.$parent.$parent.getBasePath() + scope.src )
                            .then(function () {
                                //Javascript lazy load done, calling back to parent controller, so that parent will continue with compiling other parts of the partial.
                                scope.onLoaded();
                            });
                    }
                    else
                        console.error( "Illegal src attribute in directive lazyLoad");
                }
            };
        }]);
    

    【讨论】:

    • 我试过了,但它不起作用。从svenskfotboll.se/om-svenskfotbollse/widgets/design/…获取小部件源代码
    • 请删除部分开头的“document.write()”。它应该工作。请参阅我添加到答案中的代码。使用 jQuery 的 ele.html(html) 函数将部分 html 嵌入到主机页面中。就是这样。
    【解决方案3】:

    问题的根本原因是浏览器在页面初始化后不会加载 javascript。这里有描述-Execute write on doc: It isn't possible to write into a document from an asynchronously-loaded external script unless it is explicitly opened.

    但是……

    需要在模板中加载 javascript 听起来很奇怪。 Angular 是 SPA 应用程序的框架。它们应该像您服务器的外部应用程序一样工作,并与某种 API 进行通信。

    为什么小部件包含javascript?当我访问您的链接时,我可以看到它是纯 html。如果是这样,那么您可以使用 ng-include 指令 - https://docs.angularjs.org/api/ng/directive/ngInclude

       ng-include="'path-to-template/tamplate-name.html'" 
    

    如果小部件包含一些非 Angular javascript,我建议将小部件重写为普通指令,该指令也可以与服务器通信。

    【讨论】:

    • 是的,你是对的,我知道问题所在。但我正在寻找可能性,否则我不会显示小部件。 ne-include 将不起作用,因为具有 document.write 和 second 的第三方脚本可能是跨域问题。
    • 我认为它不包括 document.write,而只是 html 本身。如果它是第三方并且不可能这样做,它只会显示一些东西并且不会干扰您的应用程序。将其粘贴到 iframe 并重复 iframe 怎么样?
    • 同样的问题,它没有加载
    • @Víťa Plšek 使用 Angularjs,即使在页面初始化之后也可以加载动态内容。即使小部件包含javascript!使用 $compile 和 angular 指令,我已经完全成功了。我已将与此问题相关的内容添加到my answer
    • 这是可能的,我的意思是这样做听起来很奇怪。
    猜你喜欢
    • 2018-04-08
    • 2019-03-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多