Всем привет. Эта статья писалась довольно долго, пару раз переписывалась заново, и, в итоге, меня не устроила. Уж слишком менторский получался тон. А тут, вдруг, грядет пятница, конец спринта, и значит, можно расслабиться. И так, не воспринимайте слишком серьезно, всего лишь несколько советов о том, как готовить фильтры в 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');
                    };
                }
            }
        }]);

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


Всем хорошей пятницы.
А как вы используете фильтры в Angular.JS?

Проголосовал 171 человек. Воздержалось 90 человек.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Комментарии (20)


  1. wir_wolf
    15.04.2016 09:45
    -4

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


    1. Drag13
      15.04.2016 10:29
      +3

      Некоторые сложности будут, но скорее от непривычки. Все пройдет. Потом :)


    1. garex
      15.04.2016 13:38

      Афтар забыл в самом начале повесить тег САРКАЗМ.


      1. Drag13
        15.04.2016 13:55

        Надеюсь :)


    1. kstep
      15.04.2016 15:14
      +2

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


  1. cyber_ua
    15.04.2016 10:11
    +1

    Добавьте тег «вредные советы»


    1. Drag13
      15.04.2016 10:27

      Добавил. Мало ли… :)


  1. DigitalSmile
    15.04.2016 10:31

    Спасибо, утащил в копилку!


    1. Drag13
      15.04.2016 11:05

      Пожалуйста :)


  1. Ohar
    15.04.2016 11:26
    -1

    Спасибо, поржал ^_^


  1. EugeneSnihovsky
    15.04.2016 13:15

    Ну не повезло автору с джунами в команде. Бывает, и не такое видели :)


  1. StGeass
    15.04.2016 13:27
    +1

    Дополню от себя встреченными на практике перлами:

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

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


    1. Drag13
      15.04.2016 13:29
      +2

      C Вашего позволения дополню статью. Тринадцатый вариант явно для настоящих самураев.


      1. StGeass
        15.04.2016 13:41

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


        Ещё добавлю от себя (по скольку тут не нашёл), что в новых версиях с фильтрами всё обстоит лучше в плане выполнения в каждом цикле, поскольку для них введено состояние 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:


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


        1. Drag13
          15.04.2016 14:05

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


          1. StGeass
            15.04.2016 14:35
            +1

            1. Для демонстрации создал этот небольшой пример, как видно на примере, изменение stateful не ведёт к вызову stateless, и наоборот, изменение stateless вызывает срабатывание stateful фильтра, при том что обычный stateless фильтр отрабатывает только один раз.


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


            1. Drag13
              15.04.2016 14:41

              Спасибо за рабочий пример, пойду покопаю.


  1. ivanuzzo
    15.04.2016 14:15
    +1

    Начиная с 7 пункта — ржал в голос :D. Автор, спасибо за статью.

    P.S. Мне и в голову не приходило, что в фильтры можно добавить trigger(«click»),
    чет я совсем отстал от жизни.


    1. Drag13
      15.04.2016 19:18
      +1

      Рад что понравилось. Хорошей пятницы)


  1. Demogor
    16.04.2016 11:10

    Ой, да зачем так все сложно-то?

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

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