Подтвердить что ты не робот

Как безопасно очистить привязку события AngularJS в директиве

У меня есть директива Angular, которая устанавливает высоту элемента, равную внутренней высоте окна браузера (+/- заданное смещение). Эта директива реагирует на событие "изменить размер" окна и соответственно регулирует его высоту. Когда масштаб моей директивы испускает событие "$ destory", я удаляю привязку к событию "изменить размер" (я думаю, что оставить его на месте вызовет некоторые проблемы, исправьте меня, если я ошибаюсь).

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

JQuery имеет концепцию пространства имен событий, которое кажется хорошим решением, но реализация Angular (JQLite) не поддерживает это. Я бы предпочел не использовать JQuery, поскольку я уже использую Angular, так что мне делать?

Здесь код для моей директивы, как сегодня

window.angular.module('arcFillClient', [])
    .directive('arcFillClientY', ['$window',
        function ($window) {

            function link($scope, el, attrs) {

                var setHeight,
                    onResize,
                    cleanUp;

                setHeight = function (offSetY) {
                    var newHeight;
                    offSetY = offSetY || 0;
                    newHeight = Math.max($window.innerHeight + parseInt(offSetY, 10)) + 'px';
                    el.height(newHeight);
                };

                onResize = function () {
                    var offset;
                    offset = attrs.arcFillClientY || 0;
                    setHeight(offset);
                };

                attrs.$observe('arcFillClientY', setHeight);
                window.angular.element($window).on('resize', onResize);

                cleanUp = function () {
                    window.angular.element($window).off('resize');
                };

                $scope.$on('$destroy', cleanUp);
            }
            return {
                link: link
            };

UPDATE Похоже на случай RTFM, но на всякий случай кто-то еще блуждает здесь, вот еще информация. Передача исходной функции (в моем случае OnResize) в .off() работает, чтобы изолировать область действия функции .off(). Из docs:

Обработчик также можно удалить, указав имя функции в аргументе обработчика. Когда jQuery {ahem... JQLite} присоединяет обработчик событий, он присваивает уникальный идентификатор функции обработчика.

Здесь обновленная функция cleanUp из моей директивы:

cleanUp = function () {
    window.angular.element($window).off('resize', onResize);
};

Спасибо tasseKATT, Karolis и Hans за ваши вклады.

4b9b3361

Ответ 1

Передайте ту же ссылку на функцию off, когда вы переходите к on:

window.angular.element($window).off('resize', onResize);

Вместо:

window.angular.element($window).off('resize');

Demo - ссылка функции передачи на выкл: http://plnkr.co/edit/1rfVPNXl6TrEcuYvzPAj?p=preview

Демонстрация - Не передавать ссылку функции на выкл: http://plnkr.co/edit/IsLqSLAzNcHqDnhMty7Q?p=preview

Демонстрации содержат две директивы, которые также прослушивают событие изменения размера окна. Используйте вертикальный разделитель между кодом и предварительным просмотром, чтобы вызвать событие.

Вы заметите, что если вы уничтожаете один, другой будет продолжать работать при передаче функции ссылки на выключение. Если вы этого не сделаете, оба перестанут работать.

Ответ 2

У меня был тот же вопрос несколько недель назад.

После просмотра источника jqLite (https://github.com/angular/angular.js/blob/master/src/jqLite.js) мы видим, что метод on добавляет событие, а метод off удаляет событие через jqLiteOff.

Глядя глубже, мы видим, что jqLiteRemoveData вызывает jqLiteOff. jqLiteRemoveData вызывается jqLiteDealoc. jqLiteDealoc вызывается в нескольких местах: jqLiteEmpty, html, replaceWith и remove. jqLiteEmpty присваивается элементу метод empty, который очищает элемент в jQuery. html, replaceWith и remove являются имитаторами jQuery.

Выполнение поиска, где remove() вызывается в элементе, мы видим, что он используется для большинства, если не для всех, логики манипуляций с DOM. Вы можете увидеть его в ngIf, ngSwitch, ngInclude и ngView.

Итак, я думаю, что Angular обрабатывает очистку прослушивателя событий, пока вы используете jqLite для присоединения событий и вызываете remove() соответствующим образом в свою собственную логику манипуляции DOM. Использование jQuery для обертывания элемента может испортить много процессов, включая очистку прослушивателя событий, но я думаю, вы уже полностью осознаете это, поскольку используете angular.element.

Ответ 3

Во-первых, нет ничего плохого в использовании JQuery и AngularJS вместе.

В стороне, что мне нравится делать, есть директива body, которая слушает window.on('resize', ...) и записывает размер в $rootScope.windowSize. Затем добавьте другую директиву в элемент $watch("windowSize", ...) и установите ширину по мере необходимости. (Фактически вам не нужно раскрывать размер в $scope - вместо этого вы можете использовать require).