Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS not cleaning child scope created by ng-include

I have the following use case - I provide a dialog service where I put a different content depending on the context. In the service method i manually compile a dom element and use it to display dialog using jquery ui. The code is the following:

var _view = jQuery('<div id="config-dialog"><span ng-include="\'' +  $scope.configView + '\'" ng-controller="' + $scope.configController + '"></span></div>');
var _compiled = $compile(_view.contents())($scope);

And then I fire a scope event that should be handled by a scope function defined in the controller

$scope.$broadcast('config-open', $scope.config);

then I open the dialog and the user performs something and closes the dialog. When the dialog gets closed i remove the "config-dialog" element from the DOM. Like this:

$(this).dialog("destroy");
jQuery('#config-dialog').remove();

However the next time the dialog gets opened and a new controller gets instantiated i see that the 'config-open' gets handled twice, when open the dialog again it gets handled 3 times. That means the scope attached to the ng-include that I dynamically create is not destroyed. I've debugged with Batarang and saw that really the child scope created by ng-include is not cleaned. AFAIK AngularJS scopes are associated to dom elements and when I remove the element, the scope should be garbage collected but this doesn't happen. My question is - is AngularJS supposed to clean up the scope in my case. What am I doing wrong and is there a more proper way to implement my use case?

like image 679
Adrian Mitev Avatar asked Sep 29 '13 12:09

Adrian Mitev


2 Answers

The controller is only for the dialog content. OK and cancel buttons for the dialog are handled outside of the dialog content

I suppose your HTML looks like this:

<div class="dialog">
    <div class="dialog-content" ng-controller="yourcontroller">
       ...your content here 
    </div>

    <button id="btnClose">Close</button>  //your button is outside your controller      
</div>

Try: angular.element(domElement).scope() Like this (using jquery with delegated event because you're creating your DOM dynamically):

$(document).on("click","#btnClose",function(){
    var dialog = $(this).closest(".dialog");
    //call this to destroy the scope.
    angular.element(dialog.find(".dialog-content")[0]).scope().$destroy();
    //or angular.element(dialog[0]).scope().$destroy(); depending on where you attach your scope.
    //Destroy dialog
    dialog.dialog("destroy");
    dialog.remove();
});
like image 123
Khanh TO Avatar answered Oct 30 '22 00:10

Khanh TO


When your dialog is closing, you should actually destroy the scope manually.

As an example, let's say you have a dom element in your dialog that has an ng-click:

<div class="dialog">
    ....
    <a data-ng-click="closeDialog()">Close Me!</a>
    ....
</div>

Then in your controller you would wire up that ng click like so:

function myController($scope, ....){
    ....
    $scope.closeDialog = function(){
        $scope.$destroy();
        //not to sure about the use of the word "this" here, but you should be able to figure out what element it is somehow
        $(this).dialog("destroy");
        jQuery('#config-dialog').remove();
    }
    ....
}

The problem I think you're encountering is that you think by destroy the element the scope is destroyed. That's not true. You have to destroy the scope manually.

like image 42
Mathew Berg Avatar answered Oct 30 '22 00:10

Mathew Berg