Top

mFilter2 своими руками

Введение

Следует понимать, что mFilter2 из коробки работает на все бабки:

  • window.history с четкой обратной связью
  • на опыте настраивается за 5 минут (не считая рутины)
  • быстро работает

Но можно все перенести в браузер.

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

Но если у вас товаров сотенка-другая или вы адепт Offline first и умеете готовить PouchDB или Dexie (локальные базы данных), то даже этой проблемы можно избежать.

Из плюсов — скорость и json. Товары приходят в json, схема данных фильтров тоже. Как бы рисуй-фильтруй — не хочу. Не хочу -)

Можно добавить анимацию фильтрации товаров, чтобы плитки так чух-чух и менялись местами (mixitUp).

Но у всего есть цена, конечно.

Мой частный случай

Мне локальная база не нужна, т.к. в категории товаров не много. Поэтому я поставил виджет отслеживания сколько сжирает js оперативки. И красивый зеленый график мне ни о чем не говорит =)) В режиме инкогнито и на телефоне типа 9 Мб занято. А в обычном режиме на десктопе с работающими всякими расширениями браузера до 150-200 бывает добегает. Но иногда до 11-13 сбрасывается. Короч, странно.

Бэкенд

MODx отдает json, содержащий список товаров, список фильтров и список seo-страниц. Последний список необязательный и состоит из url, title, текстового контента, и значений полей фильтра. Это можно сделать чтобы при фильтрации подменять h1, текст, url. А также при отрисовке для чекбоксов добавить прямую ссылку на страницу. И если бот сподобится фильтр просканировать, что получится передача веса по ссылке. Иначе стоит уповать лишь на sitemap.xml

С небольшими изменениями полученный объект я кладу в стейт от redux. В списке фильтров каждому из них я добавляю поле values с пустым массивом. В него-то мы и будем складывать значения, когда пользовать начнет тыкать по фильтру.

Как движок понимает, что надо отдать json с фильтрами?

Во-первых, я создал ресурс «api» с пустым шаблоном, где fenom мне помогает слушать $.post запросы. И дергает соответствующие сниппеты.

MODX смотрит, что у ресурса есть тв-поле со списком фильтров через запятую и отдавая разметку в шаблоне подготавливает пустой контейнер, куда мы чуть позже будем врисовывать фильтры, запрошенные отдельным запросом ajax

Список фильтров в админке я сделал с помощью migx. Прикрепил поле на главную страницу. В настройках этой tv есть название фильтра, тип (чекбокс, цена, размеры), единица изменения (например Ватты или класс водостойкости) и место расположения единицы изменения — префикс или постфикс. Это нужно, чтобы при отрисовке рядом с чекбоксом была не просто цифра, а еще и юнит, типа ◘ 600 Вт.

Обращаясь к странице за ее товарами через ajax, мы читаем тв-поле с фильтрами, бьем по запятой. Если это не «price» и не «size», то значит опции товара. Запустив сниппет «msProducts» и добавив в запрос опции, мы получаем список товаров с опциями.

Список фильтров и их подробности возьмем из migx главной страницы.

Список seo-страниц вытащим через pdoResources, фильтруя их по шаблону.

Фронтэнд

Получив json от сервера, используя его соотвествующее поле мы рисуем фильтры. Добавляем к ним слушатели-диспетчеры.

Диспатчить они будут в редюсер и в экшн-дата будут передавать название фильтра и значение. Сама фильтрация будет происходить путем накопления в массиве filters фильтрующих функций:

  • let res = filters.reduce((data, current_filter_function) => {
  • return data.filter(current_filter_function)
  • }, state.data)

А потом через reduce мы бегаем по этому массиву и поочередно применяем фильтрующие фукции к списку товаров. Т.о. мы корректно фильтруем с динамическим количеством фильтров.

Итак, элементы фильтра (чекбоксы, инпуты) будут слушать стейт. Если там в поле values появится значение или оно опустеет, то им нужно будет соотвествующим образом отреагировать.

Также помимо диспатичинга в statе, они будут смело пушить window.history.pushState.

И здесь мы работаем с URLSearchParams, методами has(), set(), toString(). Проверяем кол-во параметров и т.д. Потом уже пушим в window.history. Читать значения фильтров из subscribe нельзя, потому что будет неявная циклическая ошибка, когда мы будем слушать popstate. Поэтому только так — диспатчить одно и тоже и в стейт и в window.history

popstate

При использовании стрелок браузера мы перемещаемся по истории браузера. Обычно страница при этом перезагружается, но если мы в историю пушим объект и url, то страница не перезагрузится, а просто сообщит о том, что она могла бы. И это событие мы слушаем через window.addEventListener(«popstate», … ). В поле event.state мы увидим объект состояния, который сами туда и записали ранее.

Поэтому на такое событие мы должны отреагировать и обновить state приложения согласно этому объекту состояния. А на это событие в свою очередь отреагируют фильтры и чекнут чекбоксы или введут значения в инпуты. А также список товаров отфильтруется «сам». Таким образом кликая по стрелкам мы будем включать/выключать чекбоксы и фильтровать товары. Прям как в оригинальном mFilter2.

on page load

У нас может сложиться ситуация, что пользователь обновит страницу или вернется на нее со страницы товара и у него будут в адресной строке браузера параметры — значения наших фильтров. Эту ситуацию надо тоже оследить и сделать 2 вещи: если есть query параметры, то window.history.pushState. Потом с помощью URLSearchParams определить каким образом обновить объект filters в стейте. Таким образом при загрузке страницы мы получим сразу отфильтрованные товары согласно get-параметрам в url.

Фильтр размеров

Желая фильтровать по длине-ширине-высоте и каждое такое поле как 2 инпута «от-до», мы, во-первых, получаем довольно сложный объект в самом стейте, типа:

  • [
  • {
  • «deep»: [
  • 2,
  • null
  • ]
  • },
  • {
  • «width»: [
  • 6,
  • null
  • ]
  • }
  • ]

А, во-вторых, его приходится JSON.stringify чтобы хранить как параметр в window.history.

msProducts и json

Известно, что сниппеты MODx отдают просто строку. Далеко не всегда работает параметр return => json. Поэтому строку приходится парсить в объект. И мешается trailing comma. Ниже есть регулярка, которая зарешает:

  • $output = $modx->runSnippet(‘msProducts’, $array);
  • $output = preg_replace(‘/\,(?!\s*?[\{\[\»\’\w])/’, », $output);
  • $output = «[«.$output.»]»;

Писать-то словами такую логику долго. А уж писать код и тестить еще дольше. Но получается красиво — фильтры по длине, ширине, высоте отдельными полями «от» — «до». Тоже самое с ценой. И чекбоксы с опциями товара.

Post a Comment