2

In my application one of the custom widget uses angular functionality.

In one of the pages it is rendered via ajax, i.e. the following content is fetched via ajax and inserted into DOM properly:

_abc.html:

<script type="text/javascript">console.log(1);</script>

<script type="text/javascript" src="/assets/angular.js"></script>
<script type="text/javascript">console.log(2);</script>

<script type="text/javascript" src="/assets/angular-ui-router.js"></script>
<script type="text/javascript">console.log(3);</script>

<script type="text/javascript" src="/assets/my_widget.js"></script>
<script type="text/javascript">console.log(4);</script>
<div class="ng-app: my_widget;" id="my_widget">
.....
.....
</div>

Problem is: When this html is placed into page via ajax, only first console log is printed out, not others. Thus nothing is called after call to fetch angular.js, however the angular.js is fetched properly via ajax without any error.

Note that, when the above html is placed directly on page load instead of via ajax, everything works fine as expected.

1 Answer 1

5

There are several issues with loading Angular widget that way:

  1. You need to load and execute scripts defined in _abc.html in a correct order and scope;
  2. You should manually bootstrap Angular module to make it work (since ng-app works for only one module and only on document ready event is fired, therefore it is not suitable for dynamically loaded content);

Here is an example of how I solved these issues (see comments in the JavaScript section): http://plnkr.co/edit/ClLvhvL5435yUKOHHO1U?p=preview

index.html

...
<div id="content"></div>
...

JavaScript

$(function() {

  function loadAngularPage(url, target) {
    $.ajax(url, {
      dataType: 'html',
      type: 'GET'
    }).done(function(data) {
      var el, div = document.createElement('div'); // Create detached HTML fragment 
      div.innerHTML = data; // Populate temporary detached fragment with received HTML
      function next() {
        if(el = div.firstChild) {
            target.appendChild(el);
            if(el.tagName === 'SCRIPT') {
              if(el.hasAttribute('src')) {
                $.getScript(el.getAttribute('src'), next); // Wait until script is loaded and executed and only then process next element
              } else {
                eval(el.innerHTML); // Evaluate embedded scripts
                next();
              }
            } else {
              next();
            }
        } else {
          var match = /ng-app[:=]['"]?\s*([^;'" ]*)/gim.exec(target.innerHTML); // Get the name of angular's module to be bootstraped
          if(match && match.length === 2) {
            angular.bootstrap(target, [match[1]]); // bootstrap angular module
          }
        }
      }
      next(); // Start to process elements one by one
    });
  }

  loadAngularPage('_abc.html', document.getElementById('content')); // Load angular page

});

Note: Pay attention that /ng-app[:=]['"]?\s*([^;'" ]*)/ covers not only the flow when ng-app is defined in class of some element, but also other ways of defining main Angular module (as ng-app, x-ng-app, data-ng-app attribute, etc.). Also pay attention that Angular module is bootstrapped inside container of loaded content instead of original element inside loaded content.

_abc.html

<script type="text/javascript">console.log(1);</script>

<script type="text/javascript" src="https://code.angularjs.org/1.3.0-beta.5/angular.js"></script>
<script type="text/javascript">console.log(2);</script>

<script type="text/javascript" src="angular-ui-router.min.js"></script>
<script type="text/javascript">console.log(3);</script>

<script type="text/javascript" src="my_widget.js"></script>
<script type="text/javascript">console.log(4);</script>

<div class="ng-app: my_widget;" id="my_widget">
  Plain Text
  <div my-directive></div>
</div>

my_widget.js

angular.module('my_widget',['ui.router']).
  directive('myDirective', function() {
    return {
      template: '<div>My angular directive is working fine</div>'
    }
  });
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for the direction!! What I finally did is to move away from automatic angular bootstrapping to manual one here. So I removed ng-app, and manually bootstrapped using following code at the end of the above file: angular.element("#my_widget").ready(function() { angular.bootstrap("#my_widget", ['my_widget']); });

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.