2

I'm trying to manually compile a directive and add it to the DOM via JQuery. The directive is a simple div with an ngClick handler. No JQuery plugins are used in the directive itself (which seems to be the focus of many of the other memory leak threads).

If you run a profiler you will find that it leaks nodes. Is there something that can be done to fix this or is it a problem in JQuery/Angular?

Fiddle here
Profiler screenshot

HTML

<div ng-app="TestApp">
    <buttons></buttons>
    <div id="container"></div>
</div>

Javascript

var ButtonsCtrl = function($scope, $compile) {
    this.scope = $scope;
    this.compile = $compile;
};

ButtonsCtrl.prototype.toggle = function() {
    var c = angular.element('#container').children();

    if (0 in c && c[0]) {
        c.scope().$destroy();
        c.remove();
    } else {
        var s = this.scope.$new();
        this.compile('<thing color="blue"></thing>')(s).appendTo('#container');
    }
};

var ThingCtrl = function($scope) {};
ThingCtrl.prototype.clicky = function() {
    alert('test');
};

var module = angular.module('components', []);
module.directive('buttons', function() {
    return {
        restrict: 'E',
        template: '<button ng-click="ctrl.toggle()">toggle</button>',
        controller: ButtonsCtrl,
        controllerAs: 'ctrl'
    }
});

module.directive('thing', function() {
    return {
        restrict: 'E',
        scope: {
            color: '@'
        },
        template: '<div style="width:50px;height:50px;background:{{color}};" ng-click="ctrl.clicky()"></div>',
        controller: ThingCtrl,
        controllerAs: 'ctrl'
    };
});

angular.module('TestApp', ['components']);
2
  • What exactly is the error/leak? Please edit question and put those details in. :-) Commented Jun 24, 2014 at 17:15
  • I already mentioned that it leaks nodes. I updated the fiddle link which was initially incorrect. Click here for profiler screenshot Commented Jun 24, 2014 at 18:28

1 Answer 1

0

If you cache the template function that $compile returns, then call that with a new scope, the memory leak seems to decrease to only one node per click.

var ButtonsCtrl = function($scope, $compile) {
    this.scope = $scope;
    this.makeThing = $compile('<thing color="blue"></thing>');
    this.thingScope = null;
};

Additionally, removing the jQuery wrappers from the ButtonsCtrl.toggle() method appears to eliminate the node leak altogether.

ButtonsCtrl.prototype.toggle = function() {
    var container = document.getElementById('container');

    if (this.thingScope) {
        container.removeChild(container.childNodes[0]);
        this.thingScope.$destroy();
        this.thingScope = null;
    } else {
        this.thingScope = this.scope.$new();
        container.appendChild(this.makeThing(this.thingScope)[0]);
    }
};

See what you think.

Sign up to request clarification or add additional context in comments.

1 Comment

best to destroy the child scope before you remove it from the DOM

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.