13

I first initialize my app with ng-app="myApp" in the body tag and this works fine for all angularized-html that is loaded on first page load.

Later on I have some code that loads angularized-html in to the DOM.

In angular 1.08 I could just run angular.bootstrap($newLoadHTML, ["myApp"]) after the load and it would work; where $newLoadHTML is the newly added HTML grabbed with jQuery.

In angular 1.2 this does no longer work:(

Error: [ng:btstrpd] App Already Bootstrapped with this Element '' http://errors.angularjs.org/1.2.0-rc.2/ng/btstrpd?p0=%3Cdiv%20ng-controller%3D%22AfterCtrl%22%3E

I am getting this error which I understand, but I don't know how to solve it.

What I need to be able to do is load angularized-html and then make angular aware of it.

Here is a plunker to illustrate it: http://plnkr.co/edit/AHMkqEO4T6LxJvjuiMeT?p=preview

Christian
  • 426
  • 1
  • 5
  • 17
  • Can you reproduce that in Fiddle or Plunker? – Maxim Shoustin Oct 17 '13 at 10:52
  • http://docs.angularjs.org/api/angular.bootstrap - Not sure why you'd be grabbing HTML with jQuery and then injecting it into an Angular module definition. – m.e.conroy Oct 17 '13 at 13:38
  • Updated the question. I meant to write angular.bootstrap! It is a legacy application so that Is how it works now. Will post a Fiddle/Plunker later! – Christian Oct 17 '13 at 14:12

4 Answers4

15

I will echo what others have mentioned: this kind of thing is generally a bad idea, but I also understand that you sometimes have to work with legacy code in ways you'd prefer not to. All that said, you can turn HTML loaded from outside Angular into Angular-bound views with the $compile service. Here's how you might rewrite your current example to make it work with $compile:

// We have to set up controllers ahead of time.
myApp.controller('AfterCtrl', function($scope)  {
  $scope.loaded = 'Is now loaded';
});

//loads html and afterwards creates a controller
$('button').on('click', function() {
  $.get('ajax.html', function(data) {

    // Get the $compile service from the app's injector
    var injector = $('[ng-app]').injector();
    var $compile = injector.get('$compile');

    // Compile the HTML into a linking function...
    var linkFn = $compile(data);
    // ...and link it to the scope we're interested in.
    // Here we'll use the $rootScope.
    var $rootScope = injector.get('$rootScope');
    var elem = linkFn($rootScope);
    $('.content').append(elem);

    // Now that the content has been compiled, linked,
    // and added to the DOM, we must trigger a digest cycle
    // on the scope we used in order to update bindings.
    $rootScope.$digest();

  }, 'html');
});

Here is an example: http://plnkr.co/edit/mfuyRJFfA2CjIQBW4ikB?p=preview

It simplifies things a bit if you can build your functionality as a directive instead of using raw jQuery--you can inject the $compile and $rootScope services into it, or even use the local scope inside the directive. Even better if you can use dynamic binding into an <ng-include> element instead.

Michelle Tilley
  • 157,729
  • 40
  • 374
  • 311
  • 1
    Thanks a lot. I have not tested this on my legacy code yet, but I think this will do the job:-) – Christian Oct 20 '13 at 15:44
  • What I am doing now in the legacy code is to load the html in and run the javascript afterwards. Which makes sense with legacy jquery code. Since the dom actually needs to be there for me to grab it. Is there anyway to define the controllers afterwards and still make it work! – Christian Oct 20 '13 at 16:12
  • It is possible to lazy-load controllers, but you have to work a bit for it. Check out this article: http://ify.io/lazy-loading-in-angularjs/ – Michelle Tilley Oct 21 '13 at 02:59
  • What is going on here `var injector = $('[ng-app]').injector();`? Is that a jquery plugin? – Mild Fuzz Mar 03 '14 at 17:20
  • @MildFuzz Angular exposes a few methods to jQuery if it exists. Basically this just retrieves a reference to the injector for that particular module. – Jon Jaques Sep 30 '14 at 21:23
1

Your approach doesn't seem right. You are usinging jQuery and Angular together in an inappropriate way that is likely to have conflicts.

Angular's built in template support is the best way to do this either using ng-include or you can use Angular's routing and along with ng-view. The documentation is here:

http://docs.angularjs.org/api/ng.directive:ngInclude

http://docs.angularjs.org/api/ngRoute.directive:ngView

The simplest possible thing would be to just set the ng-include to the url string:

<div ng-include="'ajax.html'"></div>

If you actually need it to load dynamically when you do something then this is a more complete solution for you:

http://plnkr.co/edit/a9DVEQArS4yzirEQAK8c?p=preview

HTML:

<div ng-controller="InitCtrl">
    <p>{{ started }}</p> 
    <button ng-click="loadTemplate()">Load</button>
    <div class="content" ng-include="template"></div>
</div>

Javascript:

var myApp = angular.module('myApp', [])
.controller('InitCtrl', function($scope) 
{
    $scope.started = 'App is started';
    $scope.loadTemplate = function() {
      console.log('loading');
      $scope.template = "ajax.html";
    }
}).controller('AfterCtrl', function($scope) 
{
    $scope.loaded = 'Is now loaded';
});
Chris Nicola
  • 14,384
  • 6
  • 47
  • 61
  • Thx for the answer. The problem is that it is a legacy application, so it is not that easy to change the structure. It worked in 1.8 so I was thinking there must a way 1.2 too – Christian Oct 20 '13 at 03:17
1

Loading an AngularJS controller dynamically

The answer to this question fixed my problem. Since I need to create the controllers after the content was added to the DOM. This fix requires me too register controllers after I have declared it. If someone has an easier solution pleace chip in.

Community
  • 1
  • 1
Christian
  • 426
  • 1
  • 5
  • 17
0

One other gotcha that leads to this Bootstrapping error is the nginclude or ngview scenarios where your dynamic html includes script references to angular js.

My html below was causing this issue when it got injected into an existing Angular page. The reference to the angular.min.js caused Angular to rebootstrap:

<div id="fuelux-wizard" class="row-fluid" data-target="#step-container">
    <ul class="wizard-steps">
       <li data-target="#step1">
            <span class="step">1</span>
            <span class="title">Submit</span>
        </li>    
        <li data-target="#step2">
            <span class="step">2</span>
            <span class="title">Approve</span>
        </li>    
        <li data-target="#step3">
            <span class="step">3</span>
            <span class="title">Complete</span>
        </li>
    </ul>
</div>

<script src="/Scripts/Angular/angular.min.js"></script>
<script type="text/javascript">    
    angular.element('#requestMaster').scope().styleDisplayURL();
</script>
ccherwin
  • 181
  • 1
  • 4