How to bind an AngularJS controller to dynamically added HTML?

For this scenario, I have a page of HTML with some AngularJS directives, controllers, etc.

Something like this:

<html>
<body>
  <div ng-controller="myCtrl">
     <ul><li ng-repeat="item in items">{{item.name}}</li></ul>
  </div>
  <div class="placeholder">
     ...new HTML here...
  </div>
</body>
</html>

Notice that there is no ng-app directive on the page. I am not relying on auto-bootstrapping, but rather I am using the manual bootstrapping method.

angular.bootstrap(document, ['myApp']);

First, I create the module which will be bootstrapped to the document. Then as a dynamically determined list of dependencies are loaded, I attach some services, controllers, etc. Once everything is ready I call the bootstrap method.

This all works fine, until JavaScript outside of AngularJS appends to the DOM at the ...new HTML here... location. The new HTML is not processed by AngularJS.

My understanding is that you need to call $scope.$apply() or $digest() or something to get AngularJS to recognize the new HTML. However, in my example there is no controller around the new HTML coming in. The incoming HTML may look like this:

  <div ng-controller="myOtherCtrl">
     <h2>{{model.title}}</h2>
  </div>

In other words, it is relying on a different controller in the app module.

  • I can't call the angular.bootstrap method again, because the page is already bound.
  • I can't call $scope.$apply from inside the controller because the whole point is that controller code isn't being called.
  • I don't see a way to get a reference to the controller in order to re-apply or refresh it.

I have read the Ifeanyi Isitor lazy loading post and the Ben Nadel post, yet they don't seem to address HTML loaded outside the context of an existing controller.

It isn't an option for me to use AngularJS directives to handle the DOM manipulation. It is a separate part of the architecture which uses ExtJS as the framework for injecting the new DOM elements. This means I can't use ngInclude for example.

It would seem like I could just call angular.module('myApp').$refresh(); or .$apply() but that isn't an option. What am I missing?

Answers:

Answer

I've faced the same issue, here's what I came up with :

<div id="mController" ng-controller="mainController">
</div>

<div id="ee">
  2nd controller's view should be rendred here
</div>

and calling setCnt() function will inject and compile the html, and it will be linked to the 2nd controller:

var app = angular.module('app', []);

function setCnt() {
  // Injecting the view's html
  var e1 = angular.element(document.getElementById("ee"));
  e1.html('<div ng-controller="ctl2">my name: {{name}}</div>');

  // Compile controller 2 html
  var mController = angular.element(document.getElementById("mController"));
  mController.scope().activateView(e1);
}

app.controller("mainController", function($scope, $compile) {
  $scope.name = "this is name 1";

  $scope.activateView = function(ele) {
    $compile(ele.contents())($scope);
    $scope.$apply();
  };
});

app.controller("ctl2", function($scope) {
  $scope.name = "this is name 2";
});

here's an example to test this : http://refork.com/x4bc

hope this helps.

Answer

Lets take a small template

 var  template = '<div ng-controller = "someController">';
      template += '<All html you want to add>'; 
      template += '</div>';

Now if you want to add this template you have to do the following two things:

1) Instantiate your controller by $controller

2) Compile your template.

//this creates a new scope
var $scope = $rootScope.$new();  
//Controller initialize with $scope
$controller('someController',{$scope,$scope});   
var templateEl = angular.element(template);
//Now compile the template with scope $scope
$compile(templateEl)($scope);
angular.element('body').append(templateEL);
Answer

You might want to use $compile service. That's what angular do at first place.

And also this guide: http://docs.angularjs.org/guide/compiler

Answer

I had the same problem. This worked for me: https://docs.angularjs.org/api/ng/function/angular.injector

Answer

I think ng-include might help you, it loads a partial page into the element and compiles and processes it like any other part of your web page.

If This element is the main view of your web app, and you want to load different 'screens' into it, depending on the url, ng-view may come in handy.

Answer

I followed this process and it worked for me:

// Some variables
var $controllerElement = angular.element('css-selector-to-the-controller-element');
var $appElement = angular.element('css-selector-to-ng-app-element');

// compiling and applying / digesting the scope.
$appElement.injector().invoke(function($compile) {
    var scope = $controllerElement.scope();
    $compile($controllerElement)(scope);
    scope.$apply();
});

Reference: Angular.injector

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.