MODX - Вывод случайного ресурса с помощью AJAX

Александр Мальцев
Александр Мальцев
9.1K
19
Содержание:
  1. Описание процесса вывода случайной статьи на странице
  2. Код сниппета ajaxSnippet
  3. Код шаблона "Пост"
  4. Демонстрация работы созданного кода
  5. Комментарии

Урок, на котором рассмотрим, как в MODX Revolution с помощью технологии AJAX реализовать вывод случайной статьи на странице.

Описание процесса вывода случайной статьи на странице

MODX Revolution - Вывод случайной статьи на странице
MODX Revolution - Вывод случайной статьи на странице

Основные действия, которые нам предстоит реализовать, для того чтобы вывести случайную статью на странице, выразим с помощью следующих шагов:

  1. Создать на стороне клиента (с помощью скрипта JavaScript) асинхронный запрос и отправить его на сервер по адресу текущей страницы.
  2. Получить запрос, отправленный клиентом, на сервере с помощью сниппета (ajaxSnippet). В этом сниппете будем обрабатывать полученный запрос, формировать ответ и отправлять его клиенту (веб-браузеру).
  3. В этот время (после того как мы отправили запрос на сервер) страница (веб-браузер) ждёт на него ответ… Как только ответ прийдет с сервера, выводим его с помощью JavaScript на страницу.

Кроме этого выводить будем не одну случайную статью после загрузки страницы, а организуем так называемое слайд-шоу. Т.е. будем заменять одну случайную статью другой через некоторое время (например, для наглядности через 5 секунд).

Схема, описывающая процесс вывода случайной статьи на страницу
Схема, описывающая процесс вывода случайной статьи на страницу

На уровне кода необходимо будет выполнить следующее:

  • В шаблоне "Пост":
    • создать контейнер (блок div) в который будем выводить случайную статью;
    • создать js-код, который будет выполнять 3 основные действия: отправлять запрос на сервер, получать от него ответ и отображать его на странице. Данный JavaScript код создадим не в самом шаблоне, а в чанке "chunk.footer", который будет вызываться из данного шаблона;
    • поместить вызов некэшируемого сниппета "ajaxSnippet". Он будет обрабатывать на сервере запрос, поступивший от клиента и отправлять ему ответ.
  • Создать в MODX сниппет, имеющий имя ajaxSnippet. Он будет выполнять действия ("магию") вышеприведённого пункта.

Код сниппета ajaxSnippet

В сниппете для получения случайной статьи будем использовать сниппет getTicket. Получать случайную статью будем из раздела, который имеет идентификатор 4.

MODX Revolution - Создание сниппета ajaxSnippet
MODX Revolution -  Создание сниппета ajaxSnippet
<?php
// Если  запрос не AJAX (XMLHttpRequest), то завершить работу
if ($_SERVER['HTTP_X_REQUESTED_WITH'] != 'XMLHttpRequest') {return;}

// Проверяем является ли $_POST['action'] пустым. Если да, то завершаем работу
if (empty($_POST['action'])) {return;}

// В противном случае выполняем следующие действия:
// 1. Заводим переменную, которая будет хранить результат
$res = '';
// 2. Так как данный сниппет можно будет использовать и для других запросов, то выберем действие которе нужно выполнить с помощью оператора switch
switch ($_POST['action']) {
  case 'randomTicket':
    // 3. Параметры для выполнения сниппета
    $param = array(
      'sortby' => 'RAND()',
      'limit' => 1,
      'tpl'=> '@INLINE <h3>[[+pagetitle]]</h3>[[+introtext]]<p><a href="[[~[[+id]]]]" class="btn btn-primary" role="button">Читать далее...</a>',
      'parents'=> 4
    );
    // 4. Выполнить сниппет getTickets 
    $res = $modx->runSnippet('getTickets',$param);
    break;
  // При необходимости можно добавлять новые методы
}

// Если ответ не пустой, то отдаём и прерываем работу парсера MODX
if (!empty($res)) {
  // Выводит сообщение ($res) и прекращает выполнение текущего скрипта
  die($res);
}

Код шаблона "Пост"

Внесём следующие изменения в шаблон "Пост":

MODX Revolution - Изменение шаблона Пост
MODX Revolution -  Изменение шаблона Пост
  1. Поместим блок с идентификатором randomTicket в контейнер, имеющий класс col-md-4:
    <div class="col-md-4">
      [[$search]]
      <div id="randomTicket" class="thumbnail" style="padding:15px;"></div>
    
  2. Поместим вызов сниппета ajaxSnippet:
    <div class="col-md-4">
      [[$search]]
      <div id="randomTicket" class="thumbnail" style="padding:15px;"></div>
      [[!ajaxSnippet]]
    
  3. Добавим JavaScript код в чанк "chunk.footer":
    MODX Revolution - Изменение чанка chunk.footer
    MODX Revolution -  Изменение чанка chunk.footer
    <script>
    // после загрузки страницы
    $(function() {
      // вызвать функцию showRandomTicket()
      showRandomTicket();
      // смену новости будем выполнять каждые 5 секунд
      setInterval(showRandomTicket, 5000);
      // функция showRandomTicket()
      function showRandomTicket() {
        // Выполняем ajax запрос к текущей страницы (к сниппету ajaxSnippet) методом post
        $.post(document.location.href, {action: 'randomTicket'}, function(data) {
          // полученный ответ выводим в блок, имеющий идентификатор randomTicket     
          // вывод блок будем сопровождать анимацией
          $('#randomTicket').fadeOut(300, function() {
            $(this).html(data).fadeIn(800);
          });
        });
      };
    });
    </script>
    

Демонстрация работы созданного кода

Проделанную работу продемонстрируем с помощью следующего видео ролика:

Комментарии:

  1. Nik
    08 февраля 2021, 14:29
    Добрый день Александр! Делаю (пытаюсь) по образу и подобию ajax подгрузку формы с редактированием ticket — пользователями во фронтенде. Вот код:
    Сниппет Ajax
    if (empty($_SERVER['HTTP_X_REQUESTED_WITH']) || $_SERVER['HTTP_X_REQUESTED_WITH'] != 'XMLHttpRequest') {return;}
    if (empty($_POST['action'])) {return;}
    $res = '';
    switch ($_POST['action']) {
    case 'editt':
    $param = array(       
    'tid' => $modx -> resource -> id,       
    'tplFormUpdate' => 'tpl.Tickets.form.update',       
    'tplPreview' => 'tpl.Tickets.form.preview',       
    'tplImage' => 'tpl.Tickets.form.image',        
    'tplFiles' => $modx->getOption('tplFiles', $scriptProperties, 'tpl.Tickets.form.files'),       
    'tplFile' => 'tpl.Tickets.form.file',        
    'tplFormCreate' => 'tpl.Tickets.form.create',        
    'allowDelete' => '1', //нужна кнопка "удалить"       
    );
    $res = $modx->runSnippet('TicketForm',$param);
    break;
    }
    if (!empty($res)) { 
    die($res);
    }
    
    Скрипт JS
    $(document).ready(function() {
    $(document).on('click', 'a.ajax_link', function(e) 
    {
    e.preventDefault();
    var action = $(this).data('action');
    $.post(document.location.href, {action: action}, function(data) {
    $('#result').html(data);
    })
    $(document).ajaxStop(function() {    
    $( ".ajax_link" ).hide(); //скрыть кнопку "редактировать"    
    });
    return false; 
    })
    })
    
    Ссылка куда подгрузится форма
    <div id="result"></div>
    Форма редактирования подгружается, и все бы хорошо, если бы не одно но…
    Нет редактора markItUpEditor, и не загружаются картинки.
    Пытаюсь для начала разобраться с редактором markitup, и понимаю что дальше уже идет глубокое познание JS,
    чтобы реализовать это, поскольку TicketForm подключает для этого скрипты:
    jquery.markitup.js
    default.js
    Для загрузки файлов:
    plupload.full.min.js
    files.js
    Каким образом можно подключить через Ajax эти скрипты, или через Ajax проблематично это реализовать?
    Не нашел нигде подобной реализации в ticket, только попытки, как показалось безуспешные…

    1. Александр Мальцев
      09 февраля 2021, 15:00
      Здравствуйте!
      Да, TicketForm подключает скрипты. Но в данном случае это работать не будет, т.к. вы его вызываете на сервере (через AJAX). Кроме этого, для работы JavaScript скриптов Tickets нужен TicketsConfig.
      При этом JavaScript скрипты Tickets подключает не только TicketForm, но и другие сниппеты из категории Tickets, если они, конечно, имеются на странице. Тоже самое касается и TicketsConfig.
      Например, вручную добавить конфиг на страницу, если его нет, можно так:
      if (!window.TicketsConfig) {
        window.TicketsConfig = {
          ctx: 'web',
          actionUrl : '/assets/components/tickets/action.php',
          close_all_message: 'закрыть все',
          cssUrl: '/assets/components/tickets/css/web/',
          enable_editor: 1,
          jsUrl: '/assets/components/tickets/js/web/',
          tpanel: '1',
          editor: {
            ticket: [[++tickets.editor_config.ticket]], 
            comment: [[++tickets.editor_config.comment]]
          },
          source: {
            size: 3145728,
            height: 1080,
            width:1920,
            extensions: 'jpg,jpeg,png,gif'
          }
        }
      }
      
      Динамически подгрузить скрипты можно с помощью «$.getScript», но тут нужно делать проверки, а то вдруг они уже подключены к странице.
      Например, «default.js»:
      if (!window.Tickets) {
        $.getScript('/assets/components/tickets/js/web/default.js', function() {
          $('#result').html(data);
        });
      } else {
        $('#result').html(data);
      }
      
      Но, т.к. «default.js» зависит ещё от кучи скриптов, то они все тоже должны быть подключены к странице причем до «default.js». Тут либо городить кучу вложенных конструкций с использованием «$.getScript» и условий, либо просто их подключить к странице.
      Например:
      <link rel="stylesheet" href="/assets/components/tickets/js/web/editor/editor.css">
      <script src='/assets/components/tickets/js/web/lib/sortable/Sortable.min.js'></script>
      <script src='/assets/components/tickets/js/web/lib/sortable/jquery.binding.js'></script>
      <script src='/assets/components/tickets/js/web/lib/jquery.form.min.js'></script>
      <script src='/assets/components/tickets/js/web/lib/jquery.jgrowl.min.js'></script>
      <script src='/assets/components/tickets/js/web/lib/jquery.sisyphus.min.js'></script>
      <script src='/assets/components/tickets/js/web/editor/jquery.markitup.js'></script>
      <script>
      $(function() {
        $(document).on('click', 'a.ajax_link', function(e) {
          e.preventDefault();
          var action = $(this).data('action');
          $.post(document.location.href, {action: action}, function(data) {
            if (!window.TicketsConfig) {
              window.TicketsConfig = {
                ctx: 'web',
                actionUrl : '/assets/components/tickets/action.php',
                close_all_message: 'закрыть все',
                cssUrl: '/assets/components/tickets/css/web/',
                enable_editor: 1,
                jsUrl: '/assets/components/tickets/js/web/',
                tpanel: '1',
                editor: {
                  ticket: [[++tickets.editor_config.ticket]], 
                  comment: [[++tickets.editor_config.comment]]
                },
                source: {
                  size: 3145728,
                  height: 1080,
                  width:1920,
                  extensions: 'jpg,jpeg,png,gif'
                }
              }
            }
            // 
            if (!window.Tickets) {
              $.getScript('/assets/components/tickets/js/web/default.js', function() {
                $('#result').html(data);
              });
            } else {
              $('#result').html(data);
            }
          });
          $(document).ajaxStop(function() {    
            $(".ajax_link").hide(); //скрыть кнопку "редактировать"    
          });
          return false; 
        })
      })
      </script>
      
      1. Nik
        09 февраля 2021, 16:35
        Один момент, а что если к примеру скрипты подключать не на странице, а через сниппет, в приведенном примере это сниппет Ajax, через функцию, к примеру такую:
        $modx->regClientStartupScript('/assets/components/tickets/js/web/lib/jquery.form.min.js');

        Все равно ведь у нас AJAX на странице обращается к этому сниппету, и сам сниппет уже будет подключать необходимые скрипты?
        1. Александр Мальцев
          10 февраля 2021, 01:54
          Можно так, разницы никакой (только дополнительные вызовы функций). Но всё равно нужно проверять не подключён ли уже такой скрипт к странице.
          Кроме этого, единственный недостаток такого способа, в том, что нельзя указать атрибут defer, что значительно бы сократило время загрузки страницы.
          <script defer src='...'></script>
          <script defer src='...'></script>
          ...
          
          Но лучший вариант всё равно заморочиться и сделать через AJAX.
        2. Nik
          09 февраля 2021, 15:40
          Вот это интересно! Буду разбирать все по полочкам, много чего не знал из вышеприведенного, огромное спасибо!!!
          Очень надеюсь что разберусь. Markitup все таки подключить получилось как то по простому (одной строчкой) после долгих экспирементов, не подключая его на странице, а прямо в js коде. А вот с файлами тут уж конечно так просто не получится не зная капитально JS. Уже даже было желание оставить эту затею, но все таки это надо знать.
          На мой взгляд это хорошая практика по изучению JS
        3. Nik
          08 февраля 2021, 16:03
          С первым вопросом разобрался)))) Теперь выводит markitup! Все проверю, если будет нормально отпишусь как.
          1. Nik
            08 февраля 2021, 16:44
            К сожалению работает некорректно, происходит задваивание при нажатии кнопки сохранить…
            Буду думать дальше
        4. Александр
          21 сентября 2019, 23:06
          Здравствуйте! Спасибо за отличное руководство. Подскажите пожалуйста, возможно ли, для того, чтобы сократить время загрузки страницы, вызывать форму с параметрами mfilter2 через (АjaxSnippet)?
          1. Александр Мальцев
            22 сентября 2019, 01:58
            Здравствуйте! АjaxSnippet не подойдёт. Тут необходимо самостоятельное решение писать.
          2. Arhivar
            24 февраля 2018, 15:39
            Добрый день,
            Воспользовался материалами из вашей статьи и сделал своеобразный «каталог с использованием ajax»
            Вверху название категорий, на них жмешь, ниже вылезает содержание. При нажатии на категорию просто происходит ajax вызов getProducts с нужными параметрами.
            <img
            src=«https://itchief.ru/assets/uploadify/e/8/9/e89ed61a9a2c28f91f68e21fda7e7b57s.jpg» class=«fancybox thumbnail center»>


            Но потребовалась доб опция!
            Нужно сортировать полученные результаты, по цене, имени и др. Как ни пытался ничего не получается.
            Подскажите плиз, как это сделать?
            (а картинку, картинкой а не кодом можно у вас вставить?)
            1. Демьян Золин
              25 ноября 2016, 14:30
              Здравствуйте.
              Как и где добавить кнопку или ссылку «Следующая статья»
              Спасибо.
              Вывожу случайные цитаты
              Первомай
              1. Александр Мальцев
                27 ноября 2016, 04:46
                Здравствуйте. Необходимо сделать следующее:
                1. В сниппете ajaxSnippet необходимо изменить параметр tpl, а именно добавить кнопку с помощью которой пользователь будет вызывать следующую случайную запись:
                'tpl'=> '@INLINE <h3>[[+pagetitle]]</h3>[[+introtext]]<p><a href="[[~[[+id]]]]" class="btn btn-primary" role="button">Читать далее...</a> <div><button id="nextArticle" class="btn btn-danger">Следующая статья</button></div>',
                
                2. Изменить сценарий JavaScript следующим образом (т.е. добавить обработчик события click для кнопки #nextArticle):
                <script>
                $(function() {
                  //...
                  $(document).on('click','#nextArticle',function(){
                    clearInterval(intervalShowTicket);
                    showRandomTicket();
                    intervalShowTicket = setInterval(showRandomTicket, 10000);
                  });
                });
                </script>
                
              2. Демьян Золин
                18 октября 2016, 06:57
                Здравствуйте.

                Кнопку " Читать далее..." как убрать?

                Спасибо.
                1. Александр Мальцев
                  18 октября 2016, 10:56
                  Необходимо отредактировать шаблон, на основании которого осуществляется вывод:
                  'tpl'=> '@INLINE <h3>[[+pagetitle]]</h3>[[+introtext]]<p><a href="[[~[[+id]]]]" class="btn btn-primary" role="button">Читать далее...</a>',
                  
                  Т.е. в качестве значения оставить только:
                  'tpl'=> '@INLINE <h3>[[+pagetitle]]</h3>[[+introtext]]<p>',
                  
                2. Легион
                  22 июня 2016, 22:24
                  <div class="col-md-4">
                    [[$search]]
                    <div id="randomTicket" class="thumbnail" style="padding:15px;"></div>
                    [[!ajaxSnippet]
                   
                  Пропущена закрывающая скобка. Кстати, запустить мне так и не удалось выборку. Возможно потому, что пока только две тестовые новости.
                  1. Александр Мальцев
                    23 июня 2016, 14:37
                    Спасибо, исправил.
                    Посмотрите, что у Вас возвращается в браузере на вкладке Network.
                  2. Андрей
                    22 февраля 2016, 00:18
                    Всем привет! Подскажите, как на основании этой статьи реализовать, допустим, галерею случайных статей с фото и интротекстом? Я не понимаю, как это сделать для вывода фото с помощью ms2Gallery. На данный момент у меня это реализовано без аякса таким кодом:
                    <h3 class="widget_title">Случайные статьи</h3>
                    <ul>
                      [[!getTickets?
                        &parents=`7` 
                        &depth=`1` 
                        &tpl=`tpl.random.post` 
                        &includeTVs=`tags` 
                        &processTVs=`1`
                        &includeContent=`1`
                        &limit=`3`
                        &sortby=`RAND()`
                        &hideContainers=`1`
                        &loadModels=`ms2gallery` 
                        &leftJoin=`{
                          "120x90": {"class":"msResourceFile","alias":"120x90", "on": "120x90.resource_id = Ticket.id AND 120x90.path LIKE '%/120x90/' AND 120x90.rank=0"}
                          }`
                        &select=`{"Ticket":"*","120x90":"120x90.url as 120x90"}`
                        &showLog=`0`
                      ]]
                    </ul>
                    
                    Спасибо.
                    1. Александр Мальцев
                      22 февраля 2016, 09:56
                      Всё тоже самое.
                      Необходимо только изменить параметры в коде сниппета ajaxSnippet в п.3.
                      1. Андрей
                        23 февраля 2016, 12:20
                        Сделал сниппет в пункте 3 такого содержания
                        // 3. Параметры для выполнения сниппета
                            $param = array(
                              'sortby' => 'RAND()',
                              'limit' => 3,
                        	  'depth' => 1,
                        	  'hideContainers' => 1,
                        	  'processTVs' => 1,
                        	  'includeContent' => 1,
                        	  'includeTVs' => 'tags',
                        	  'loadModels' => 'ms2gallery',
                              'tpl'=> '@INLINE <li class="clearfix">
                          <div class="scale_image_container">
                        	<a href="[[~[[+id]]]]"><img src="[[+120x90]]" alt="[[+pagetitle]]" class="scale_image"></a>
                        	<div class="post_image_buttons">
                        	  <a href="#" class="icon_box">
                        		<i class="fa fa-file-text"></i>
                        	  </a>
                        	</div>
                          </div>
                          <div class="post_text">
                        	<a href="[[~[[+id]]]]"><h4>[[+pagetitle:ellipsis=`20`]]</h4></a>
                        	<div class="event_date">[[+date_ago]]</div>
                          </div>
                        </li>',
                              'parents'=> 7,
                        	  'leftJoin'=> '{"120x90": {"class":"msResourceFile","alias":"120x90", "on": "120x90.resource_id = Ticket.id AND 120x90.path LIKE '%/120x90/' AND 120x90.rank=0"} }',
                        	  'select'=> '{"Ticket":"*","120x90":"120x90.url as 120x90"}'
                            );
                        Ошибка 500, страница не грузится
                        И почему-то конфликтует скрипт с jquery.tweet.js, который у меня в шаблоне
                    Войдите, пожалуйста, в аккаунт, чтобы оставить комментарий.