13

I have script block to load the widget on partial view but I am getting below error if i placed directly on partial html page:

"Failed to execute 'write' on 'Document': It isn't possible to write into a document from an asynchronously-loaded external script unless it is explicitly opened."

sample script block is :

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

I will have multiple widget so i want something to load through ng-repeat. Please advise.

Note I tried to display static way for this I add a html page with html/body tag and placed that above script block inside then load that page through the iframe then it works. But the issue it's very hard the update the content before loading into the iframe

Parwej
  • 580
  • 9
  • 30

3 Answers3

1

Without seeing some of the content of the external widget, this is hard to diagnose exactly. My first impression is that it may need to be altered to return a document, which you would then assign in a JS script block using the name of the header function within the external widget. Something along the lines of:

var someDoc = writeDoc();

Where writeDoc() is the header function in the widget which returns, I assume, an HTML doc of some sort. The script tag must come before your declaration, of course. Alternatively, with your ng-repeat requirement, you might use something like:

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

In a list block or even a select block (although I wouldn't recommend something like that), depending on how much space the document takes up.

Hope that helps.

-C§

CSS
  • 412
  • 5
  • 17
  • Posted script block already there – Parwej Aug 04 '15 at 06:42
  • Script block already posted there. Yes you are right, when I put the same script on index file then it's loading correctly. But when put on partial view then it's not. Two work around 1. Put script block in html file and then load in iframe. But issue is, this script is the user base so i need to change html file dynamically. its not fisible solution. 2. Second solution is as you suggested need to modify the content, so instead the script block download the content from the url and then remove the document.write show on the page. But issue is CROS domain request they will not allowed.1 – Parwej Aug 04 '15 at 07:03
  • Are you credentialing on the index page or in a process that leads to it? Disallowance sounds like you need to be loading dummy credentials or having the user sign-on and persist the credentials to the call in your index.html for this widget. – CSS Aug 04 '15 at 14:44
  • My requirement is simple: support user1 have two widget and user2 having 3 widget so if user1 is loggedin then load two widget on partial page. – Parwej Aug 04 '15 at 18:02
1
  1. Use $compile service to compile the html contents that you dynamically loaded, AND at the same time, bind it to a scope.
  2. Would suggest you make a directive. You can pass in the url of partial using an attribute.

Particularly the directive's link property is like this:

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

See the answer to this question for details, it has provided a plunker demo.

Hope this helps.

EDIT I would like write more in response to @Víťa Plšek - angular.cz's answer. I once did something even more dynamic, which is to load javacsript in the partial!

Once upon a time, I made a one-page application based on angularjs. Each menu function is a partial dynamically loaded using ajax. Naturally, each menu function is a separate angular controller. To make the codes easier to manage, I did not merge all controllers' codes in one file and load them all at once from index.html. Instead, I let each partial specify its own controller's js code using a custom directive and do lazy-load.

So the main page of the one-page application use a custom directive to contain dynamically loaded partial:

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

, while each partial is stored in a separate html file. Its contents are like this:

<lazy-load-script src="js/controllers/dynamic-controller1.js" >
</lazy-load-script>

<div ng-controller="DynamicController1">
    ...
</div>

Now, there is one issue: If we compile the partial file all at once, there will be an error saying DynamicController1 is not defined. It is true. We have to loaded dynamic-controller1.js first, and then compile the part <div ng-controller="DynamicController1"></div> after the js file has finished loading.

So the codes of directive DynamicPartial are like this:

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

And the codes of <lazy-load-script>are as follows. The crux is to use an onLoaded scope attribute to specify a callback to parent controller.

"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");
            }
        };
    }]);
Community
  • 1
  • 1
gm2008
  • 4,245
  • 1
  • 36
  • 38
  • I tried but it's not working. Taking the widget source from http://svenskfotboll.se/om-svenskfotbollse/widgets/design/?scr=table&ftid=57108 – Parwej Dec 12 '15 at 10:26
  • Please remove the 'document.write()' at the start of your partial. It should work. See the codes I have added to my answer. Use jQuery's `ele.html(html)` function to embed the partial html into the host page. That's it. – gm2008 Dec 14 '15 at 09:31
0

Root cause of your problem is that browser will not load the javascript after page initialization. It is described here - Execute write on doc: It isn't possible to write into a document from an asynchronously-loaded external script unless it is explicitly opened.

But ...

Need for loading javascript in template sounds strange. Angular is framework for SPA application. They should work like external application for your server and communicate with some kind of API.

Why does widget contains javascript? When I access your link, I can see it is plain html. If so, then You can use ng-include directive - https://docs.angularjs.org/api/ng/directive/ngInclude

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

If widget contains some non-angular javascript I would suggest that widgets should be rewritten to normal directives, which can also communicate with server.

Community
  • 1
  • 1
  • Yes you are right and I know the issue. But i am looking possibilities otherwise I am not going to show the widget. ne-include will not work because that third party script having the document.write and second may be cross domain issue. – Parwej Aug 08 '15 at 12:46
  • I ment it like not including document.write, but just html itself. If it is third party and not possible to do that, and it only shows something and not interfere with your app. What about pasting it into iframe and repeat iframes ? – Víťa Plšek - angular.cz Aug 08 '15 at 13:39
  • same issue, it's not loading – Parwej Aug 08 '15 at 13:48
  • @Víťa Plšek With Angularjs, it is possible to load dynamic contents even after page intialization. Even if widget contains javascript! Using $compile and angular directive, I have been totally successful in this. I have added contents in relation to this issue to [my answer](http://stackoverflow.com/a/34202313/2197555) – gm2008 Dec 14 '15 at 09:39
  • That is possile, My point was that it sounds strange to do this. – Víťa Plšek - angular.cz Dec 14 '15 at 15:03