How to remove observers created by angular directives after compilation

I had some performance problems when opening modals multiple times in angular, and I found out that every time the dialog service is asked to create a new modal, the number of observers in the area increases dramatically.

DialogService is as follows:

.factory("DialogService", function($q, $compile){
        return {
            toast:function(text){
                Dialog.toast(text);
            },
            alert:function(text){
                var deferred = $q.defer();
                var d = Dialog.alert(text);
                d.bind("hide", function(){
                    deferred.resolve();
                });
                return deferred.promise;
            },
            showModal:function(options){
                var dialog = new Dialog(options);
                dialog.show(function(){
                    var self = this;
                    if ( "scope" in options ) $compile(self.contentLayer)(options.scope);

                    options.scope.dismiss = function(){
                        dialog.hide();
                    }   
                });
                if ( "hide" in options ){
                    dialog.bind("hide", options.hide);
                }
            },...

The problem I am facing is that every time a new modal is created, it changes the scope with new observers when I use directives.

I fixed most of the problem by adding observers that remove observers when node is destroyed in my user directives, but ng-repeat, ng-if, etc. inside modal content, observers are constantly added every time showModal is called.

, .

, , , .

+4
1

, , ,

-, , , , , .

, , $compile , , .

:

angular.module("app", [])
    .factory("DialogService", function($q, $compile){
        return {
            toast:function(text){
                Dialog.toast(text);
            },
            alert:function(text){
                var deferred = $q.defer();
                var d = Dialog.alert(text);
                d.bind("hide", function(){
                    deferred.resolve();
                });
                return deferred.promise;
            },
            showModal:function(options){
                var childScope;
                var dialog = new Dialog(options);
                dialog.show(function(){
                    var self = this;    
                    if ( "scope" in options ){
                        var childScope = options.scope.$new();
                        $compile(self.contentLayer)(childScope);

                        options.scope.dismiss = function(){ 
                            dialog.hide();
                        }

                        dialog.bind("hide", function(){
                            childScope.$destroy();
                        });
                    }   
                });
                if ( "hide" in options ){
                    dialog.bind("hide", options.hide);
                }   
            },
            confirm:function(text){
                var deferred = $q.defer();
                Dialog.confirm(text, function(){
                    deferred.resolve();
                }, function(){
                    deferred.reject();
                });
                return deferred.promise;
            }
        }
    })
;

Extra

, , , , , .

, , ; , , :

var watchers = [
     $scope.$watch(...),
     $scope.$watch(...),
     ...
];
...
var observer = new MutationObserver(function(mutations) {
    if (!document.body.contains($element[0])){
        observer.disconnect();
        dropdown.remove();
        for ( var i = 0; i < watchers.length; i++ ){
            watchers[i]();
        }
        $scope.$destroy();
        return;
    }
});  
var config = { childList: true, subtree: false  /*attributes: true, characterData: true*/ };
observer.observe(document.querySelector('body'), config);

, ? ... , - ( ):

var clickHandler = function(event){
    var isChild = $($element).has(event.target).length > 0 || $(dropdown).has(event.target).length > 0;
    var isSelf = $element[0] == event.target || dropdown == event.target;

    $scope.$apply(function(){
        if (!isChild && !isSelf) {
            $scope.mdSelectCtrl.dismiss();
        }
    }); 
}

$document.bind('click', clickHandler);

click $, click ( , ); , 10-15 , , $ , , , .

: DOM, :

var observer = new MutationObserver(function(mutations) {
    if (!document.body.contains($element[0])){
        observer.disconnect();
        dropdown.remove();
        for ( var i = 0; i < watchers.length; i++ ){
            watchers[i]();
        }
        $scope.$destroy();
        $document.unbind('click', clickHandler);
        return;
    }
});  

, , , MutationObserver , , node, :

$scope.$on("destroy")

- , .

+1

Source: https://habr.com/ru/post/1665032/


All Articles