Skip to content

Instantly share code, notes, and snippets.

@umidjons
Created August 12, 2014 05:14
Show Gist options
  • Star 14 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save umidjons/1fb8ae674df4c71f85cf to your computer and use it in GitHub Desktop.
Save umidjons/1fb8ae674df4c71f85cf to your computer and use it in GitHub Desktop.
Compile dynamic template from outside of angular.

Compile dynamic template from outside of angular

I want to load dynamic HTML content via AJAX, then compile it, because it contains angular directives.

<!DOCTYPE html>
<html ng-app="app">
<head>
	<title>Compile dynamic HTML</title>
	<script type="text/javascript" src="js/jquery-1.11.0.js"></script>
	<script type="text/javascript" src="js/angular.js"></script>
</head>
<body ng-controller="TestCtrl">

<div obj></div>

<button class="foo">Change</button>

<script type="text/javascript">
	var app=angular.module('app',[]);
	app.controller('TestCtrl',function($scope){});

	jQuery(document).ready(function($){
		$('.foo').on('click',function(e){
		  // todo: load HTML content via AJAX or generate via jQuery
		  // todo: compile new HTML content, which contains Angular directives
		});
	});
</script>
</body>
</html>

We can get reference to $compile outside of angular via injector().invoke().

	jQuery(document).ready(function($){
		$('.foo').on('click',function(e){
			e.preventDefault();
			angular.element(document).injector().invoke(function($compile){
				var obj=$('[obj]'); // get wrapper
				var scope=obj.scope(); // get scope
				// generate dynamic content
				obj.html($('<input type="text" ng-pattern="/^([0-9]+)$/" ng-model="test"><span>{{test}}</span>'));
				// compile!!!
				$compile(obj.contents())(scope);
			});
		});
	});
@UltCombo
Copy link

Nice!
Btw, I also needed a scope.$digest() after $compile to get things working smoothly.

@RPDeshaies
Copy link

+1 for @UltCombo comment

Here is a little class that I did that does the trick

/**
 * AngularHelper : Contains methods that help using angular without being in the scope of an angular controller or directive
 */
var AngularHelper = (function () {
    var AngularHelper = function () { };

    /**
     * ApplicationName : Default application name for the helper
     */
    var defaultApplicationName = "myDefaultAppName";

    /**
         * Compile : Compile html with the rootScope of an application
         *  and replace the content of a target element with the compiled html
         * @$targetDom : The dom in which the compiled html should be placed
         * @htmlToCompile : The html to compile using angular
         * @applicationName : (Optionnal) The name of the application (use the default one if empty)
         */
    AngularHelper.Compile = function ($targetDom, htmlToCompile, applicationName) {
        var $injector = angular.injector(["ng", applicationName || defaultApplicationName]);

        $injector.invoke(["$compile", "$rootScope", function ($compile, $rootScope) {
                        //Get the scope of the target, use the rootScope if it does not exists
            var $scope = $targetDom.html(htmlToCompile).scope();
            $compile($targetDom)($scope || $rootScope);
            $rootScope.$digest();
        }]);
   }

    return AngularHelper;
})();

And you can call like :

$.ajax("myTemplate.html"), {
                success : function(view){
                    //Append the template
                    $("body").append("<div id='my-template-scope'>");
                    var $targetDom = $("#my-template-scope");

                    //Compile it to bind it with angular
                    AngularHelper.Compile($targetDom, view)
                }
            });

The only problem with this is that I can't make this code work if the template contains ng-include. Maybe someone has the answer for this...

@frangeris
Copy link

@UltCombo that's exactly what I'm looking, paste the line and it work's like a charm... :D

@hayatbiralem
Copy link

Thanks a lot! This is exactly what I was looking for.

@spederiva
Copy link

thanks! this is exaclty what I was looking for!

@sivakumarbdu
Copy link

Thanks a lot! Its really awesome...!

@DeeDi
Copy link

DeeDi commented Mar 2, 2016

You save my world!! Thank you so much!!

@aoussarmustapha
Copy link

@RPDeshaies thank you so much, but I get this error:
$targetDom.html(...).scope is not a function

@agnosco
Copy link

agnosco commented May 16, 2016

I get error while getting scope. The error is:
Uncaught Error: [ng:areq] Argument 'scope' is required

@kahboom
Copy link

kahboom commented Jun 2, 2016

This is good, thank you. My only issue is that I can't get it too work with Angular form validation and it also throws the following error:

TypeError: text.indexOf is not a function .. at $interpolate

And same issue with not being able to use it with ng-include, unfortunately.

@AshfaqMemon
Copy link

How can we do same thing with Angular 2? Any idea on this?

@kodero
Copy link

kodero commented May 29, 2017

Nice, helped my day.

@gaojianzhuang
Copy link

Sorry, this way cannot work in my scenario.
Current Page:
<html>
<head>test</head>
<body ng-app="core">
<div id="AjaxContent"></div>
<input type="button" id="loadContentButton" />
<script> var coreModule = angular.module("core", []); var module = angular.module("myTest", []);
module.controller("MyTestController", ["$scope", function($scope) {}]);

$(function() {
$("#loadContentButton").on("click", function() {
$.ajax({
url: "AjaxContent.html",
success: function(view) {
var ajaxContentElement = $("#AjaxContent");
ajaxContentElement .html(view);
angular.element(ajaxContentElement ).injector().invoke(["$compile", function ($compile) {
var scope = ajaxContentElement .scope();
$compile(ajaxContentElement )(scope);
scope.$apply();
}]);
}
});
});
});
</script>
</body>
Ajax Content:
<div ng-app="myTest" ng-controller="MyTestController">
this is my test page.
</div>

This will throw the js error:
Uncaught Error: [$injector:unpr] Unknown provider: lengthCountLeftFilterProvider <- lengthCountLeftFilter

Please help anyone can.Thx so much!

@trainoasis
Copy link

I have the same issue as @agnosco.
Uncaught Error: [ng:areq] Argument 'scope' is required
Any ideas guys?

@umrtariq
Copy link

In angularjs controller

scope.htmlVaribale = '<h1>Hello</h1><p>This is Html</p>';

In Html

<div ng-bind-html="htmlVaribale"></div>

Result

Hello
This is Html

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment