I guess due to misuse the following piece of code creates a memory leak, I can't understand why, I've recorded short screencast to demonstrate the problem, you can find it here: https://www.youtube.com/watch?v=IWCcOI5kN1c&feature=youtu.be
Here is the failing code, what it does is it just dynamically compiles the html stored in variable called content
, but as soon as you start changing content
variable via the textarea, the code starts creating $watch'es the situations gets even worser as soon as you increase the amount of bindings, for each binding Angular creates new scope (which is obviously right) but it doesn't remove the old ones and they are kept in the memory
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<script data-require="angular.js@1.3.x" src="https://code.angularjs.org/1.3.15/angular.js" data-semver="1.3.15"></script>
<script>
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
});
app.directive('compile', function($compile) {
return {
restrict: 'A',
link: function(scope, elem, attrs) {
scope.$watch(attrs.compile, function(newVal) {
// create a span (an inline element) so we have an actual DOM node to
// set the innerHTML of.
var newElem = document.createElement('span');
newElem.innerHTML = newVal;
// clear out the contents of this element
elem[0].innerHTML = '';
// and replace it with the raw (uncompiled) node
elem[0].appendChild(newElem);
// now the node is in the DOM so we can compile it
// but we want to use a try..catch because the user
// might be in the middle of typing a new expression,
// but the syntax right now is not valid so the
// expression parser will throw an error.
try {
// compile the node in the DOM with the existing scope
$compile(newElem)(scope);
} catch(e) { /* don't need to do anything here */
}
});
}
};
});
</script>
</head>
<body ng-controller="MainCtrl" ng-init="variable = 3; content = '{{ variable }}'">
<div>
The value of $scope.variable === "{{ variable }}"
</div>
<div>
The value of $scope.content === "{{ content }}"
</div>
<br>
<div>
The value of $scope.content is <b>ng-model</b>'ed via the following textarea:<br>
</div>
<textarea rows="3" ng-model="content"></textarea>
<div style="border: 1px solid black">
Instead of rendering the value of the $scope.content field which is currently equal to "{{ content }}" I need to render compiled and evaluated value which should be equal to "{{ variable }}"
</div>
<hr>
<p>Look! It works: <span compile="content"></span></p>
</body>
</html>
Okay, I apologise, I had no idea how bad the memory leak was! (It was my directive Lu4 was using)
I believe I have now got rid of the leak and cleaned up a lot better. I created a new child scope each time I compiled anew and destroyed the old one first. And as per this excellent tip I made sure to destroy the scope before clearing out the DOM element.
Updated Plunkr