Фильтруй правильно, или вредные советы по фильтрации в Angular.Js



    Всем привет. Эта статья писалась довольно долго, пару раз переписывалась заново, и, в итоге, меня не устроила. Уж слишком менторский получался тон. А тут, вдруг, грядет пятница, конец спринта, и значит, можно расслабиться. И так, не воспринимайте слишком серьезно, всего лишь несколько советов о том, как готовить фильтры в Angular.JS

    Кому интересно или хочется немного расслабиться — вперед под кат и всем хорошей пятницы!

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

    2. Делайте фильтры универсальными. Фильтр должен уметь фильтровать все. От товаров до embedded base-64. Всего один фильтр, каких-то двадцать параметров и ваша команда счастлива. Ведь все работает «из коробки», и писать ничего не надо. А если этот фильтр умеет еще и текст переводить в нижний регистр, то признание вас найдет. Неизбежно.

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

    4. Давайте фильтрам только общие названия. А вдруг его кто-то потом сделает универсальным! Не переименовывать же потом! К тому же так вашим коллегам будет гораздо интереснее читать разметку. Можно сказать, квест им подарите. Бесплатный.

    5. Infinitie Scroll должен быть реализован только на фильтрах и никакой подгрузки данных. Сходили на сервер, выгрузили всю таблицу, а дальше пусть наши фильтры трудятся. Не зря же вы их писали. Нагрузка на сервер меньше, хостинг можно взять подешевле, а вам «благодарочка».

    6. Бизнес логика должна быть только в фильтрах. Потому что – «И» — Инкапсуляция.

    7. Вообще прячьте в фильтры всю логику. Так надежнее. А если не получается, то хотя бы самую «тяжелую». И чем тяжелее, тем лучше. Можно там всякие сложные расчеты производить или DOM менять. А еще лучше сервер дергать. Прямо из фильтра. Пользователь не поезд, подождет! Зато у вас будут самые тонкие сервисы и контроллеры. Как раз похвастаетесь на следующем собеседовании.

    8. Кстати о DOM. Кто сказал, что для манипуляций с ним нужно использовать директивы? Фильтры, это лучшее место для $('#user_icon').trigger('click');

    9. Коммуницируйте фильтры. Пусть один фильтр, что-то меняет во втором фильтре. Лучше всего через $broadcast. И лучше всего входящие данные. Так дебаг превратиться в веселую и занимательную игру, развивающую внимание, концентрацию и знание основ ядра Angular. И вообще, всегда используйте $broadcast. Это стильно.

    10. Собирайте фильтры в цепочки! Если в цепочке меньше трех фильтров это не кошерно! Можно даже предложения фильтрами писать. Для коллег или просто любопытных пользователей.

    11. Комбинируйте фильтры с ng-mouse-over. Это весело! Можно даже в хендлере mouse-over ничего не писать. Главное повесьте его на body и зачекиньте в пятницу вечером. Особенное удовольствие получите, если у вас Continues Integration.

    12. Не оптимизируйте. Во-первых, ранняя оптимизация убивает. Во-вторых, это работа фреймворка. Вы же не будете делать чужую работу, правильно?

    @By StGeass

    13. Используйте фильтры как хэндлеры при изменении дайджеста!

    14. Изменяйте контекст всего $scope прямо внутри фильтра через this. Ваша версия ангуляра не даёт этого сделать? Прокидывайте контекст прямо внутрь через аргументы и изменяйте снова!

    И главное, знайте. Не фильтры тормозят ваше приложение. А непонимание и недооценка Вас, как разработчика!

    Для любопытных.
    Фильтры в Angular это такая функция, которая вызывается каждый дайджест. Т.е. часто, очень часто, и несколько раз подряд. Контроля над началом фильтрации нет, контроля над окончанием нет. Поднимать события бесполезно и чревато. При неосторожном обращении легко превращает приложение в тыкву. А, да, «из коробки» инструментов для оптимизации фильтров тоже нет.

    Для более любопытных.
    Победить это не просто и каждый находит свое решение для конкретного случая. Например фильтрацией на стороне, использование ng-change, isDirty, INotifyOnPropertyChange, кешированием, $scope.$watch(function getData(){}, true) и так далее. Хотя последнее я вам не советую ибо это может быть даже хуже. Еще говорят CodeReview помогает, но это вообще шаманство.

    Для самых любопытных.
    .directive('onRepeatFinish', [function () {
                return {
                    restrict: 'A',
                    link: function (scope, elem, attr) {
                        if (scope.$last === true) {
                                console.log('ngRepeatFinished');
                        };
                    }
                }
            }]);
    

    Если что, вот «это» для параметризованных фильтров не работает.


    Всем хорошей пятницы.

    Only registered users can participate in poll. Log in, please.

    А как вы используете фильтры в Angular.JS?

    • 20.8%Использую как описано в советах.40
    • 27.1%Использую с осторожностью.52
    • 28.1%Стараюсь избегать.54
    • 24.0%Как то не задумывался.46
    Инфопульс Украина
    Creating Value, Delivering Excellence
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 20

      –4
      Такое чуство что автор стати фильтровый маньяк. Эдак и бизнес логику, и запросы к серваку, все в фильтрах. Бесспорно, фильтры классная вещь, но это уже черезчур. Человек который пройдет в команду с таким проектом явно будет иметь некие сложности с освоением структуры проекта.
        +3
        Некоторые сложности будут, но скорее от непривычки. Все пройдет. Потом :)
          0
          Афтар забыл в самом начале повесить тег САРКАЗМ.
            0
            Надеюсь :)
            +2

            В сети всегда найдётся человек, который воспримет сарказм дословно.

            +1
            Добавьте тег «вредные советы»
              0
              Добавил. Мало ли… :)
              0
              Спасибо, утащил в копилку!
                0
                Пожалуйста :)
                –1
                Спасибо, поржал ^_^
                  0
                  Ну не повезло автору с джунами в команде. Бывает, и не такое видели :)
                    +1
                    Дополню от себя встреченными на практике перлами:

                    13. Используйте фильтры как хэндлеры при изменении дайджеста!

                    14. Изменяйте контекст всего $scope прямо внутри фильтра через this. Ваша версия ангуляра не даёт этого сделать? Прокидывайте контекст прямо внутрь через аргументы и изменяйте снова!
                      +2
                      C Вашего позволения дополню статью. Тринадцатый вариант явно для настоящих самураев.
                        0

                        Да, пожалуйста.


                        Ещё добавлю от себя (по скольку тут не нашёл), что в новых версиях с фильтрами всё обстоит лучше в плане выполнения в каждом цикле, поскольку для них введено состояние stateless, по умолчанию все фильтры являются именно такими.


                        Приведу выдержку из так и не опубликованной статьи (хотя могу и полностью опубликовать, если для кого-то это будет полезным):


                        Динамические фильтры


                        Здесь речь пойдёт о фильтрах преимущественно завязанных на данных приходящих "извне" и не зависящих от выражения (expression) к которому привязан фильтр (filter), например, фильтр который использует внутри себя некий сервис.


                        В версии до 1.3 фильтры были довольно глупыми и навешивая на выражение вотчер они постоянно заново вычисляли результат этого фильтра. Это одна из причин почему не стоит использовать множество фильтров в одном месте, нагружая лишней работой цикл $digest и одна из причин, почему новые фильтры работают быстрее. В новой версии фильтры ведут себя куда умнее и не вычисляют значение фильтра до тех пор, пока не изменится выражение к которому привязан данный фильтр.


                        Однако возникает проблема, как обновить значение фильтра, если оно зависит не только от выражения, но и от каких-то внешних факторов? Иными словами, как вернуть фильтру своё старое, "глупое" поведение, когда нам это нужно?


                        Для этого в 1.3 были введены понятия статичного (stateless) и динамического (stateful) фильтров.
                        По умолчанию фильтр ведёт себя как stateless. Сменить его поведение можно выставив нашему фильтру флаг $stateful в true.


                        Пример:


                        angular.module('myApp', [])
                            .filter('customFilter', ['someService', function (someService) {
                                function customFilter(input) {
                                    // манипуляция данными сторонним сервисом someService
                                    input += someService.getData();
                                    return input;
                                }
                        
                                customFilter.$stateful = true;
                        
                                return customFilter;
                            }]);

                        Breaking change:


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

                          0
                          Возможно я чего то понимаю но:
                          1. Я сейчас сижу на 1.4.8, статью перед публикацией проверял. Любой дайджест заставит выполниться фильтр. В том числе дайджест инициированный в другом контроллере. В чем улучшение?
                          2. Что должно заставить фильтр, манипулировать входящими данными? Почему нельзя обработать данные сервисом, и отдать фильтру только финальную последовательность?
                            +1
                            1. Для демонстрации создал этот небольшой пример, как видно на примере, изменение stateful не ведёт к вызову stateless, и наоборот, изменение stateless вызывает срабатывание stateful фильтра, при том что обычный stateless фильтр отрабатывает только один раз.


                            2. Ну на вскидку, у нас есть динамический список №1 и в одном из мест где его надо выводить, есть некий чёрный список №2 через который этот первый список фильтруется, при этом сам чёрный список тоже зависит от того что у нас в №1. Разумеется, всё это можно написать так, чтобы отдать в фильтр конечные данные, а не вызывать получение чёрного списка №2 внутри непосредственно фильтра, но случаи разные бывают, как и извращенцы, поэтому такая возможность разработчиками учтена :)
                              0
                              Спасибо за рабочий пример, пойду покопаю.
                      +1
                      Начиная с 7 пункта — ржал в голос :D. Автор, спасибо за статью.

                      P.S. Мне и в голову не приходило, что в фильтры можно добавить trigger(«click»),
                      чет я совсем отстал от жизни.
                        +1
                        Рад что понравилось. Хорошей пятницы)
                        0
                        Ой, да зачем так все сложно-то?

                        while (true){var callLogic=()=>{… callLogic();}; setInterval(()=>callLogic (),1)}

                        Нет cpu-нет проблем.

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