Делегирование событий в AngularJS. Попытка разобраться

    Евгений Гришковец, спектакль 'Как я съел собаку'Гришковец как бы говорит нам «А давайте разберемся»

    В комментариях к статье мною был задан вопрос о делегировании в AngularJS в контексте того, что привычный многим императивный способ назначения обработчика для группы элементов аля

    $('ul').on('click', 'li', function(){ // обработчик ....
    

    не совсем приемлем в декларативном AngularJS. Собственно, последовал совет использовать свою директиву, решающую проблему делегирования. На самом деле, на ум мне ничего толкового не пришло, и я решил погуглить, наткнулся на такой вариант: создаем директиву, которую необходимо установить на родительский для группы нужных нам элементов узел, который регистрирует слушателя.
    Вот демо.
    А вот 2 интересующие нас части кода:
    html
    <ul bn-delegate="li a | selectFriend( friend )">
     
            <li ng-repeat="friend in friends">
     
                <!-- Delegate target. -->
                <a href="#">{{ friend.name }}</a>
                <!-- Delegate target. -->
     
            </li>
     
        </ul>
    


    //js
                        element.on(
                            "click.bnDelegate",
                            selector,
                            function( event ) {
     
                                // Prevent the default behavior - this is
                                // not a "real" link.
                                event.preventDefault();
     
                                // Find the scope most local to the target
                                // of the click event.
                                var localScope = $( event.target ).scope();
     
                                // Invoke the expression in the local scope
                                // context to make sure we adhere to the
                                // proper scope chain prototypal inheritance.
                                localScope.$apply(
                                    function() {
     
                                        expressionHandler( localScope );
     
                                    }
                                );
     
                            }
                        );
    


    Но подождите. В зависимостях получается jQuery (можно обойтись без него, но тем не менее), но это пол беды. В дебрях директивы все тот же императивный подход. Собственно, в этот момент я решил опубликовать пост, с попыткой разобраться в подходах к делегированию в Angular, в комментарии призываются все, кому есть что сказать по этому поводу.

    В попытке прояснить ситуацию наткнулся на вот такое issue. На просьбу пользователя запилить возможность автоматического привязывания события к родительскому элементу, при использовании ng-repeat, с мотивацией более лучшей производительности был отправлен за анализом производительности.

    image

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

    Если взвесить за и против делегирования, то получается
    Плюсы:
    • Упрощает инициализацию и экономит память: не нужно вешать много обработчиков.
    • Меньше кода: при добавлении и удалении элементов не нужно ставить или снимать обработчики.
    • Удобство изменений: можно массово добавлять или удалять элементы путём изменения innerHTML.


    Минусы:
    • Во-первых, событие должно всплывать. Большинство событий всплывают, но не все.
    • Во-вторых, делегирование создает дополнительную нагрузку на браузер, ведь обработчик запускается, когда событие происходит в любом месте контейнера, не обязательно на элементах, которые нам интересны.
      Но обычно эта нагрузка невелика и не является проблемой.



    На самом деле сколько-нибудь приближенных к реальности тестов я придумать не смог, на элементарных примерах разницы не видно абсолютно. Поэтому призываю в комментариях обсудить, так ли необходимо юзать делегирование?

    Источники:
    www.bennadel.com/blog/2448-Using-jQuery-Event-Delegation-In-AngularJS.htm
    github.com/angular/angular.js/issues/1568
    learn.javascript.ru/event-delegation
    Share post
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 20

      +2
      Делегирование нужно использовать там, где это действительно необходимо. Бесконечная прокрутка — хороший тому пример. Вот только я бы отказался от такого явного использование селекторов, как-то это не вписывается в идеологию ангуляра :)

      Я бы сделал это как-то так. К этому решению бы добавить возможность явно указать какие типы событий мы хотим делегировать, но что-то меня в этом решении смущает.
        +1
        В общем я пока прихожу к выводу, что пока я не увижу проблем с производительностью, заморачиваться не стоит. А как начнутся — создам ишью в репозитарии angular, пусть у разработчиков болит голова :)
          0
          Ну, кхм, такой подход тоже сомнителен, разработчики вам ничем не обязаны. В любом случае решение проблемы кроется в осознании того, что ангуляр не серебряная пуля и использовать его надо с умом, как и любой другой инструмент :) Прошу прощения, если не распознал в последнем предложении вашего комментария сарказм.
            0
            Del
              +1
              Не сарказм, просто шутка, даже смайл поставил. Да ангуляр не серебрянная пуля, но он мне прям очень по душе пришелся (сам не пойму, почему), а в купе с тем, что фронтенд не является моей сильной стороной — пытаюсь подходить в основательно, разглядывать границы применения, чтоли.
            0
            Это имеет смысл если вы еще по многим другим причинам используете jQuery в вашем проекте. А иначе лучше только средствами AngularJS обойтись и не подключать jQuery вообще.
          +4
          Вы не указали самый главный минус подхода назначения событий в jQuery: события которые вешаются подобным образом непонятно откуда применяются и непонятно к чему (то есть разобраться потом очень проблематично человеку со стороны). Чему собственно и является противоположным декларативный подход AngularJS.

          Непонятно почему вас не устроил вариант с директивой. Или даже просто назначение ng-click внутри ng-repeat. Не могли бы вы описать поподробнее? (с примерами)
            0
            Del
              +4
              Ну пока что да, я за самый простой подход с ng-click, вроде codepen.io/anon/pen/Fobmf/
              Если коротко, то вот (какой-то неудобный Codepen):
              $scope.selectFriend = function( index ) {
              
                   $scope.selectedFriend = $scope.friends[index];
              
               };
              

              <ul>
                  <li ng-repeat="friend in friends">
                      <a href="#" ng-click="selectFriend( $index )">{{ friend.name }}</a>
                  </li>
              </ul>
              


              По поводу того, что не указал главный минус, я написал:
              привычный многим императивный способ назначения обработчика для группы элементов

              не совсем приемлем в декларативном AngularJS


              Почему не подошел способ с директивой? Честно говоря, имхо, я решил что это оверхед для меня в данный момент.
                +2
                Тоже придерживаюсь такого подхода, гораздо нагляднее и вроде как angular way. Директиву обычно не считаю лишним оверхедом, но зачастую просто лень для простых случаев ее использовать.
                  +1
                  Вы можете модифицировать переменные родительского scope через $parent. То есть достаточно такого кода:

                  <ul>
                      <li ng-repeat="friend in friends">
                          <a href="#" ng-click="$parent.selectedFriend = friend">{{ friend.name }}</a>
                      </li>
                  </ul>
                  

            • UFO just landed and posted this here
                +1
                Попробуйте Ember.js ради добра:
                Ember delegates all events to the application's root element (usually the document body) using jQuery. When an event occurs, Ember identifies the nearest view that handles the event and invokes its event handler.
                  0
                  Ох, незнаю. Пробежался по Ember, выглядит неплохо. Но дело в том, что надо же на чем-то остановиться. Мне по душе пришелся декларативный подход.
                +1
                Делегирование борется со следствием, а не с причиной. Обычная причина это куча элементов (и обработчиков) на странице. Поэтому решать нужно ее, удаляя элементы вне экрана.
                  +1
                  Имхо, подкрепленноенулевым опытом: это до определенной степени несемантично, что ли. Если существует список из n позиций, то он им и должен быть. Да и удалять-добавлять элементы в «viewport» тоже, в общем-то, накладно.
                    +1
                    Это так же семантично как обычная пагинация. Другое дело, что «лучшие умы» заняты написанием очередного фреймворка в 30 строчек кода, а до решения действительно сложной и необходимой задачи так ни у кого руки не дошли.

                Only users with full accounts can post comments. Log in, please.