AngularJS: отвязывание обработчиков и рассуждение об опасностях использования вложенных контроллеров
В нашем приложении (чего уж тут скрывать — http://m.mamba.ru, тач-версия) всё построено на встроенных в angular маршрутах.
При этом, как известно, шаблон страницы отрисовывается внутри тега ng-view.
Появилось несколько страниц, на которых внутри контента (это важно) нужно показывать баннер.
Сделал я это так:
внутри списка пользователей в html:
1 2 3 4 5 |
<div ng-switch="$index"> <div class="b-content__iframe" ng-controller="BannerMiddleCtrl" ng-switch-when="4"> <inline-ad frame-src-base="http://mamba.ru/be.phtml" position="91" show-ad="showAd"></inline-ad> </div> </div> |
в контроллерах:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
controller('BannerMiddleCtrl', ['$scope', '$rootScope', function BannerMiddleCtrl($scope, $rootScope) { $rootScope.$on('$routeChangeSuccess', function (event, current, previous) { setCanShowAd(); }); $rootScope.$watch('showPopupTip + controllerDataLoaded + controllerDataLoadError + routeError + hidePageForPopup', function () { setCanShowAd(); }); function setCanShowAd() { $scope.showAd = canShowAd(); $rootScope.middleAdShown = $scope.showAd; } function canShowAd() { if (<сложное бизнес-условие>) { return true; } return false; } }]). |
И заметил я, что после показа страницы, на которой используется этот контроллер, функция setCanShowAd() продолжает вызываться и на других страницах. И ещё забавнее, что вызываться она будет несколько раз, если несколько раз зайти на эту страницу.
Что, в общем-то, логично, хотя и печально. Получается, что AngularJS сам не снимает обработчики событий для таких контроллеров (возможно, и для обычных контроллеров тоже), что, наверное можно объяснить и идеологическими соображениями и сложностью реализации.
Поэтому родилась следующая идея (не без помощи http://stackoverflow.com/questions/14957614/angular-js-clear-watch).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
angular.module('CommonControllers', []). value('BannerMiddleCtrlWatchers', {}). controller('BannerMiddleCtrl', ['$scope', '$rootScope', function BannerMiddleCtrl($scope, $rootScope) { BannerMiddleCtrlWatchers.rsOnUnbind = $rootScope.$on('$routeChangeSuccess', function (event, current, previous) { setCanShowAd(); }); BannerMiddleCtrlWatchers.rsWatchUnbind = $rootScope.$watch('showPopupTip + controllerDataLoaded + controllerDataLoadError + routeError + hidePageForPopup', function () { setCanShowAd(); }); function setCanShowAd() { $scope.showAd = canShowAd(); $rootScope.middleAdShown = $scope.showAd; } function canShowAd() { if (<сложное бизнес-условие>) { return true; } return false; } }]). |
app.js
1 2 3 4 5 |
app.run(['$rootScope', 'BannerMiddleCtrlWatchers', function($rootScope, BannerMiddleCtrlWatchers) { $rootScope.$on('$routeChangeSuccess', function (event, current, previous) { BannerMiddleCtrlWatchers.rsOnUnbind && BannerMiddleCtrlWatchers.rsOnUnbind(); BannerMiddleCtrlWatchers.rsWatchUnbind && BannerMiddleCtrlWatchers.rsWatchUnbind(); }); |
Идея в том, чтобы отвязывать обработчики событий на rootScope.
О том, как сделать правильно — в следующем выпуске :)
Similar Posts
LEAVE A COMMENT
Для отправки комментария вам необходимо авторизоваться.