Наверх

Простой AJAX-фильтр для MODX с помощью pdoPage

Да, ещё один способ реализации Ajax-фильтра на сайте =)

Начнём с простого вывода на странице обычного списка ресурсов. Обязательно добейтесь, чтобы нормально работала AJAX-пагинация, потому что своих механизмов работы с AJAX мы писать не будем, а будем использовать методы AJAX, которые есть в pdoPage.

<div id="pdopage">
  <div class="rows row">
  {'!pdoPage' | snippet : [
    'ajaxMode' => 'default',
    
    'parents' => 0,
    'limit' => 3,
    
    'includeTVs' => 'height,weight,speed_type',
    'tvPrefix' => '',
    
    'tpl' => '@INLINE 
      <div class="col-sm-6 col-md-4">
        <div class="thumbnail">
          <div class="caption">
            <h3>{$pagetitle}</h3>
            <p>Вес: {$weight}</p>
            <p>Высота: {$height}</p>
            <p>Тип: {$speed_type}</p>
          </div>
        </div>
      </div>',
  ]}
  </div>
  {'page.nav' | placeholder}
</div>


Для фильтров я буду использовать такую вёрстку. Но вёрстка тут особой роли не играет — главное указать правильные классы в JS-коде.

<form class="form-horizontal" id="filters" action="">
  <div class="form-group">
    <label class="col-sm-1 control-label">Вес</label>
    <div class="col-sm-5">
      <input type="number" name="weight" class="form-control" placeholder="47">
    </div>
    <label class="col-sm-1 control-label">Высота</label>
    <div class="col-sm-5">
      <input type="number" name="height" class="form-control" placeholder="120">
    </div>
  </div>
  
  <div class="form-group">
    <label class="col-sm-1 control-label">Тип</label>
    <div class="col-sm-11">
      <div class="btn-group" data-toggle="buttons">
        <label class="btn btn-default">
          <input type="radio" name="speed_type" value="fast"> Быстрые
        </label>
        <label class="btn btn-default">
          <input type="radio" name="speed_type" value="slow"> Медленные
        </label>
      </div>
    </div>
  </div>
</form>

JS-код обработки фильтров выглядит как-то так:

$(document).on('change keyup', '#filters input', function(){
    // Проверяем, что pdoPage подключён
    if (typeof(pdoPage) == 'undefined') return;
    
    // Собираем значения всех фильтров в единый массив
    var fields = {};
    $.each($('#filters').serializeArray(), function(){
        fields[this.name] = this.value;
    });
    
    // И отправляем этот массив на сервер.
    $.post(document.location.href, {
            action: 'filter',
            fields: fields,
            // Параметр hash - обязательный (он содержит настройки pdoPage)
            hash: pdoPage.configs.page.hash
        }, function(data) {
            // Просим pdoPage загрузить новый список ресурсов
            var tmp = document.location.href.split('?');
            pdoPage.keys.page = 0;
            pdoPage.loadPage(tmp[0], pdoPage.configs.page);
        });
});

Уже сейчас при изменении значения фильтров список ресурсов будет обновляться. Теперь осталось «объяснить», как обрабатывать фильтры. Для этого создаём плагин на событие OnHandleRequest:

<?php
if ($modx->context->key == 'mgr' || empty($_SERVER['HTTP_X_REQUESTED_WITH']) || $_SERVER['HTTP_X_REQUESTED_WITH'] != 'XMLHttpRequest') return;
switch ($_POST['action']) {
    case 'filter':
        $output = array('success' => false, 'message' => '');
        
        // Проверяем, что hash получен и параметры pdoPage существуют
        if (isset($_POST['hash']) && !empty($_POST['hash'])
            && isset($_SESSION['pdoPage'][$_POST['hash']])
            && !empty($_SESSION['pdoPage'][$_POST['hash']])) {

            $hash = (string) $_POST['hash'];
            
            // Указываем только ТВ, доступные для фильтрации
            $tvs = array('height','weight','speed_type');
            
            // Наполняем условие выборки
            $where = array();
            foreach ($tvs as $tv) {
                if (isset($_POST['fields'][$tv]) && $_POST['fields'][$tv] !== '') {
                    $where[$tv] = $_POST['fields'][$tv];
                }
            }
            
            // Добавляем это условие в параметры pdoPage "на лету"
            $_SESSION['pdoPage'][$hash]['where'] = $where;
            
            $output['message'] = $where;
            $output['success'] = true;
        } else {
            $output['message'] = 'Error';
        }
        echo $modx->toJSON($output);
        die();
        break;
    default:
        break;
}

Теперь наши фильтры работают. Единственное — pdoPage «не знает», что показывать, когда ни один результат не найден. Чтобы такой случай учесть, создадим файл pdopage.custom.js и укажем его в параметре frontend_js:

{'!pdoPage' | snippet : [
    'ajaxMode' => 'default',
    'frontend_js' => '/assets/components/pdotools/js/pdopage.custom.js',

    'parents' => 0,
    'limit' => 3,
    // ...
}

В стандартном коде мы добавим только условие else на случай, если ответ от сервера будет пустым:

// ...
pdoPage.loadPage = function (href, config, mode) {

    // ...

    $.post(config['connectorUrl'], params, function (response) {
        if (response && response['total']) {
            // ...
        } else { // Добавляем условие else
            wrapper.find(rows).html('Ничего не найдено');
            wrapper.find(pagination).html('');
            wrapper.removeClass('loading');
            wrapper.css({opacity: 1});
            if (config['mode'] == 'default') {
                $('html, body').animate({scrollTop: wrapper.position().top - 50 || 0}, 0);
            }
        }
    }, 'json');
};
// ...

На этом простой AJAX-фильтр готов.


10 комментариев

  1. Алексей Архипенко 11 марта 2019, 14:39(Комментарий был изменён) # 0
    Что-то не работает фильтр, а куда последний код добавлять нужно?

    С Уважением,

    Алексей
    1. Илья Уткин 11 марта 2019, 14:50 # 0
      Надо скопировать стандартный JS-файл от pdoTools и в этой копии внести нужные изменения.
    2. Ishvan 07 апреля 2019, 14:10 # 0
      Уже сейчас при изменении значения фильтров список ресурсов будет обновляться. Теперь осталось «объяснить», как обрабатывать фильтры. Для этого создаём плагин на событие OnHandleRequest:
      вот дошел до сюда… но не происходит изменений в списках ресурсов
      1. Илья Уткин 08 апреля 2019, 07:08 # 0
        Изменений и не должно быть. Список должен просто обновляться — «мигать».
      2. Илья 20 апреля 2019, 23:39 # 0
        Огромное спасибо. Метод рабочий.
        Можете помочь с дополнением таким.
        Добавил в вызов условие:
        &where=`[[!#GET.where]]`
        В него передаются параметры:
        <div class="dropdown-menu">
        <a class="dropdown-item" href="[[~[[*id]]]]">Все</a>
        <div class="dropdown-divider"></div>
        <a class="dropdown-item" href="[[~[[*id]]]]?where=team_p1 > team_p2">Победы</a>
        <a class="dropdown-item" href="[[~[[*id]]]]?where=team_p1 = team_p2">Ничьи</a>
        <a class="dropdown-item" href="[[~[[*id]]]]?where=team_p1 < team_p2">Поражения</a>
        </div>
        
        Т.е. сравниваются значения ТВ у ресурса и выводятся как надо.
        Получается ваш метод работает как Ajax, а мои ссылки перезагружают страницу и фильтр по ajax скидывается.
        Вот мне нужно это всё как то соединить. Помогите с решением.
        1. Илья Уткин 22 апреля 2019, 15:52 # 0
          Вам нужно переделывать ваши ссылки на AJAX-поля, как speed_type в примере.
          1. Илья 23 апреля 2019, 09:13(Комментарий был изменён) # 0
            Добрый день!
            Нужна помощь.
            Фильтр переделал.
            Сезон - Вид игры - Турнир - это фильтр
            Ниже должно отображаться, то что выбрали в фильтре, т.е. например так?
            Фильтр: 2018, Регби-15, Чемпионат Москвы
            И ниже:
            Всего матчей: 22
            Побед: 10
            Ничьих: 5
            Поражений: 7
            А только потом уже результаты… Готов отблагодарить.
        2. Илья 22 апреля 2019, 16:03 # 0
          Спасибо за ответ. Так и сделал.
          1. Дмитрий 07 июня 2019, 18:09 # 0
            А как тут сделать ценовой ползунок?
            1. Николай 12 июня 2019, 19:43 # 0
              Спасибо большое за такое замечательное и функциональное решение!!!
              Подскажите, пожалуйста, а как сделать так, чтобы результат запроса подгружался не «на лету», а после нажатия кнопки? но при этом страница полностью не перезагружалась?

              Авторизация

              через сервис Loginza:

              Подписка или RSS

              Буду присылать новые статьи — никакого спама



              Шаблоны MODX

              1 2 Дальше »

              Объектная
              модель
              MODX