Слушатели Angular $ rootScope $ on в «разрушенном» контроллере продолжают работать

У меня есть приложение angular, настроенное с помощью директивы ng-view в index.html, с представлениями, настроенными с конфигурацией $routeProvider, и каждое представление имеет свой собственный контроллер. В одном контроллере представления у меня есть прослушиватель $rootScope.$on, который следит за событиями. Я ожидаю, что этот слушатель больше не будет работать, когда я меняю представления, но это не так. Часы по-прежнему продолжают вызывать код при запуске, даже когда я переключился на представление, которое больше не использует этот контроллер.

Я настроил некоторый тестовый код для прослушивания события $destroy, и я вижу, что событие $destroy запускается, но функция слушателя все еще работает. Я распечатал $scope.$id как в функции наблюдения, так и в прослушивателе событий $destroy, и вижу, что идентификаторы совпадают. Таким образом, кажется, что даже после того, как событие $destroy происходит для определенной области, прослушиватель $on на нем все еще продолжает работать. Почему это так? У меня сложилось впечатление, что при переходе к другому представлению или в любом случае контроллер уничтожается, тогда прослушиватели для него очищаются.

Plnkr: http://plnkr.co/edit/TxssxQJY5PVLTAIAr4xD?p=preview

var animateApp = angular.module('animateApp', ['ngRoute', 'ngAnimate']);

animateApp.config(function($routeProvider) {
    $routeProvider
        .when('/', {
            templateUrl: 'page-home.html',
            controller: 'mainController'
        })
        .when('/about', {
            templateUrl: 'page-about.html',
            controller: 'aboutController'
        })
        .when('/contact', {
            templateUrl: 'page-contact.html',
            controller: 'contactController'
        });

});

angular.module('animateApp').service('service', ['$interval', '$rootScope', service]);
function service ($interval, $rootScope) {
    var service = {};
    service.abc = 1;
        $interval(function() {
        service.abc++;
        $rootScope.$broadcast('service.abc', service.abc);
    }, 500);

    return service;

}


animateApp.controller('mainController', function($scope, $rootScope, service) {
    $scope.pageClass = 'page-home';
    $scope.$on('$destroy', function iVeBeenDismissed() {
                        console.log('goodbye ' + $scope.$id);
      // release resources, cancel request...
    })

    $rootScope.$on('service.abc', function (newval) {
        console.log($scope.$id);
    })
});

animateApp.controller('aboutController', function($scope) {
    $scope.pageClass = 'page-about';
});

animateApp.controller('contactController', function($scope) {
    $scope.pageClass = 'page-contact';
});

person dz210    schedule 28.12.2016    source источник
comment
можете ли вы продемонстрировать свою логику/проблему с помощью plunk или скрипка   -  person Minato    schedule 28.12.2016
comment
Предоставьте минимальный воспроизводимый пример   -  person TheSharpieOne    schedule 28.12.2016
comment
Извините, на самом деле это с прослушивателем $rootScope.$on. Я обновлю с помощью plnkr и отредактирую.   -  person dz210    schedule 28.12.2016
comment


Ответы (2)


Таким образом, вопрос был полностью изменен, так что контекст был совершенно другим, поэтому мой предыдущий ответ больше не относится к вопросу на самом деле, за исключением того, что он применяется точно так же.

Вы можете отменить регистрацию своего слушателя в том же отношении, что и мой ответ ниже.

$rootScope.$on() возвращает функцию отмены регистрации.

Следовательно, ваш mainController можно переписать как:

animateApp.controller('mainController', function($scope, $rootScope, service) {
    $scope.pageClass = 'page-home';
    var unregister = $rootScope.$on('service.abc', function (newval) {
        console.log($scope.$id);
    });
    $scope.$on('$destroy', unregister);
});

ПРЕДЫДУЩИЙ ОТВЕТ

Вы должны убедиться, что вы отвязываете свои часы, когда область вашего контроллера уничтожена.

При создании часов angular возвращает функцию отвязки для созданных вами часов. Это довольно подробно описано здесь.

Создайте ссылку на часы, которые вы создаете:

var unbinder = $scope.$watch("foo.bar", doStuff);

Следите за разрушением вашего прицела, как и прежде, но вызывайте туда свой unbinder.

$scope.$on("$destroy", function(){
    if(typeof unbinder === "function")
        unbinder();
});

Как только будет вызвана ваша функция unbinder, ваши часы будут удалены.

person WebWanderer    schedule 28.12.2016
comment
@ dz210 Пожалуйста, перечитайте мой ответ, так как я изменил его, чтобы он соответствовал вашим изменениям. Пожалуйста, будьте более конкретными с вопросами в будущем. - person WebWanderer; 28.12.2016
comment
Спасибо за ответ, но мой вопрос, в частности, заключается в том, почему слушатели продолжают работать после того, как контроллер кажется уничтоженным, поэтому я ищу не решение, а объяснение того, что происходит. К сожалению, я уже знаю, как отменить регистрацию слушателей и часов. Мой вопрос более теоретический, так как я не смог найти ответ в документах angular. - person dz210; 28.12.2016
comment
Тогда @ dz210 ваш ответ заключается в том, что $rootScope является глобальным для всего вашего приложения, и регистрация прослушивателя для $rootScope не имеет привязки к вашим изолированным областям, если вы не сделаете это вручную. Если вы поместите свой слушатель в свою изолированную область, например, в $scope.$on, тогда ваш слушатель будет уничтожен, когда область будет удалена. В вашем вопросе не указано, что вам интересно, почему больше, чем вам интересно, как. - person WebWanderer; 28.12.2016

Вы должны добавить прослушиватель событий $destroy в свои контроллеры:

    scope.$on('$destroy', function () {
      element.off('blur'); // example
      element = null;
    });

При этом вы вручную убедитесь, что на неактивных контроллерах больше не будет активных $watchers.

person Tome Pejoski    schedule 28.12.2016