Основы Ajax


Ajax — это метод асинхронного запроса к серверу. Текущая страница html с помощью скрипта обращается к серверу с определенным вопросом, получает ответ и что-то с ним делает. Обычно — вставляет результат в страницу.

Как это сделать?

Итак, пишем простейший сниппет для получения запроса:

<?php
// Откликаться будет ТОЛЬКО на ajax запросы
if (empty($_SERVER['HTTP_X_REQUESTED_WITH']) || $_SERVER['HTTP_X_REQUESTED_WITH'] != 'XMLHttpRequest') {return;}

// Сниппет будет обрабатывать не один вид запросов, поэтому работать будем по запрашиваемому действию
// Если в массиве POST нет действия - выход
if (empty($_POST['action'])) {return;}

// А если есть - работаем
$res = '';
switch ($_POST['action']) {
	case 'helloWorld':
		$res = 'Hello World!';
		break;
	// А вот сюда потом добавлять новые методы
}

// Если у нас есть, что отдать на запрос - отдаем и прерываем работу парсера MODX
if (!empty($res)) {
	die($res);
}

Тут все очевидно: сниппет ловит ajax запросы (а мы все делаем через jquery, который любезно шлет серверу заголовок HTTP_X_REQUESTED_WITH), проверяет, задано ли действие и пытается дать для него ответ.

Пару слов о прерывании парсера. Этот метод я придумал сам, и мне кажется, он гораздо удобнее, нежели использование отдельного php файла.

Лично мне нравится, что мои сниппеты в зависимости от запроса могут работать и через ajax, и просто при обычной загрузке, не завися ни от кого.

Классический же метод состоит в том, чтобы завести отдельный скрипт на сервере, который будет принимать все ajax запросы, и отвечать на них.
Или создать отдельную страницу, сделать к ней пустой шаблон, запихать туда опять же, один сниппет и прописывать в нем все методы ответов.

В общем, решайте сами, как вам больше нравится, статья моя — пишу как делаю я.

Страница и скрипт

Конечно, мы будем использовать jQuery. Скрипт просто в теле страницы.
<!-- Наш сниппет, при обычной загрузке он ничего не делает -->
[[!Ajax_test]]

<!-- Подключаем jquery с сервера Яндекса -->
<script type="text/javascript" src="http://yandex.st/jquery/1.7.1/jquery.min.js"></script>
<!-- Наш скрипт запроса и обработки -->
<script type="text/javascript">
$(document).ready(function() {
	// Вешаем обработчик события "клик" на все ссылки с классом ajax_link
	$('a.ajax_link').click(function() {
	
	// Берем действие из атрибута data-action ссылки
	var action = $(this).data('action');
	
	// Ajax запрос к текущей страницы (а на ней наш сниппет) методом post
	$.post(document.location.href, {action: action}, function(data) {
		// Выдаем ответ
		alert('Запрос успешно выполнен')
		$('#result').html(data);
	})
	
	// Не даем ссылке кликнуться - нам же не нужна перезагрузка страницы?
	return false;
  })
})
</script>

<!-- Ссылка с нужным классом и data - атрибутом, с действием -->
<a href="#" data-action="helloWorld" class="ajax_link">Привет, мир!</a>

<!-- html элемент для вставки ответа от php -->
<div id="result"></div>
Можно копипастить в контент страницы.

Итог

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

Дальше нужно набивать новые методы в сниппет (вызов wayfinder, например), расставлять ссылки по страницам и развешивать обработчики событий на них.

Можно добавить к ссылке новый атрибут data-update="" и писать туда имя элемента для обновления. А при получении ответа вставлять его не в #result, как железно забито сейчас, а в указанный элемент. Тогда вы одними ссылками можете сделать сайт полностью на ajax.

Чуть изменить js скрипт и ссылку попробуйте сами.

Надеюсь, теперь, принцип ясен.
18 june 2012, 11:56    Василий Наумкин   G+  
24    9346 0

Comments (82)

  1. Николай Мордынский 10 july 2012, 17:44 # 0
    Василий, добрый день можете пояснить 1 момент.

    // Берем действие из атрибута data-action ссылки
    var action = $(this).data('action');

    Ведь по идее .data('data-action') надо запрашивать судя по описанию функции

    в чем фокус? ))
    1. Василий Наумкин 10 july 2012, 20:37 # 0
      Код более важен, чем комментарии.
      1. Иван Брежнев 11 july 2012, 11:48 # 0
        Не пойму что тут не понятно! несколько раз перечитывал, все номрально написано. И правильно не .data('data-action'), а .data('action')
        1. Да я и не говорил, что в коде ошибка. Я его прочел и понял просто хочется понять не поверхностно.

          Я не профи в программировании и просил пояснить работу функции.

          Я первым делом полез посмотрел спецификацию по функции .data('name') в таком случае вернется значения параметра нейм.
          А по сути в коде возвращается событие

          те action выходит как зарезервированный в функции параметр или что?

          Извиняюсь за глупые вопросы
          1. Василий Наумкин 17 july 2012, 16:18 # 0
            Вам нужно почитать тут api.jquery.com/data/

            Есть такой параметр data, через тире указывается его имя, а через равно — значение.

            То есть, data-action=«getList» получается в jQuery как
            var action = $('selector').data('action');

            Вот и все.
      2. Антон Гавриченко 14 july 2012, 16:46 # 0
        Добрый день. Спасибо за статью. Подскажите пожалуйста как вызвать wayfinder с помощью ajax? Т.е. со страницы с помощью ajax передаю параметр в обработчик. В обработчике нужно в зависимости от этого параметра вызвать wayfinder с startId(изменяется для различного входного параметра). Как этот вызов wayfinder'a передаётся на начальную страницу.

        case '5':
        $params = array();
        $start = 1;
        $params[«startId»]=$start;
        $res = $modx->runSnippet('Wayfinder', $params);

        У меня возвращает пустое значение.
        Спасибо.
        1. Василий Наумкин 14 july 2012, 17:31 # 0
          Вроде все верно. Смотрите логи на ошибки, и пробуйте вызвать так же, только без Ajax.

          Вывести результат то не забыли?
          echo $res; die;
          1. Антон Гавриченко 14 july 2012, 18:32 # 0
            Всё действительно отлично. Спасибо. Искал ошибку не там. Не было дочерних ресурсов в одном из списков, оттого и на выходе было пустое значение.
        2. Tom Tom 07 september 2012, 01:06 # 0
          Я не силен в программировании, простите за глупый вопрос.
          «Ответ сервера» формируется в сниппете с помощью die($res), и, по видимому, подхватывается методом $.post() автоматически.
          А как этим способом обратиться к двум разным скриптам на одной странице?
          Т.е. как различить ответ сниппета А и сниппета Б?
          1. Василий Наумкин 07 september 2012, 05:10 # 0
            После die() другие сниппеты не работают. Это будет единственный ответ.

            Ваши 2 разных скрипта должны реагировать на 2 разных action — для того оно и надо.
          2. Владимир Красиков 26 november 2012, 18:14 # 0
            Добрый день. Василий, спасибо за статью. Пытался сделать функционал по аналогии с Вашим решением. Сервер шлет в ответ браузеру на XMLHttpRequest запрос 200ok, а данные не приходят. Попробовал применить Ваш код без изменений, ситуация аналогичная. То есть перезагрузки не происходит, сообщение: «Запрос успешно выполнен» выводится, а «helloWorld» нет. В чем может быть ошибка? MODx — Evolution.
            1. Владимир Красиков 27 november 2012, 08:24 # 0
              Сейчас проверил. Если просто создать отдельный файл в корне, то ответ нормально приходит. Если делать через MODx, то в ответ приходит полная html страница. Получается, что сниппет не отрабатывает?
              1. DJNew 13 december 2012, 15:15 # 0
                чтобы не приходил весь html — выведи вызов сниппета на отдельную страницу с пустым шаблоном.
              2. Владимир Красиков 13 december 2012, 16:06 # 0
                Это понятно. Но тогда не получится одной страницей обрабатывать обычные и AJAX запросы, как это было описано в данной статье.
                1. Виталий Воропаев 13 december 2012, 16:43 # 0
                  Получится, только нужна проверка на отлов асинхронных запросов, далее (аз). Если сервер не обнаружил в заголовке «аз», то отдать все остальное, а если обнаружил обработать полученный запрос, отдать результат и сделать обрыв.

                  Где-то Уважаемый Василий об этом писал и пример обрыва парсера тоже присутствовал.
                  1. Сергей Савельев 03 february 2013, 02:27 # 0
                    Наконец освоил Ваш урок. Низкий поклон.

                    Хотелось бы привести пример:

                    Воспользовавшись сниппетом getResourceField вывести данные поля нужного ресурса

                    Код сниппета ajax:
                    // Откликаться будет ТОЛЬКО на ajax запросы
                    if ($_SERVER['HTTP_X_REQUESTED_WITH'] != 'XMLHttpRequest') {return;}
                    
                    // сниппет будет обрабатывать не один вид запросов, поэтому работать будем по запрашиваемому действию
                    $action = $_POST['action'];
                    
                    // Если в массиве POST нет действия - выход
                    if (empty($action)) {return;}
                    
                    //А если есть - работаем
                    $res = '';
                    switch ($action) {
                      case 'field': 
                          $params = array();
                          $params['id'] = $_POST['id'];
                          $params['field'] = $_POST['field'];
                          $res = $modx->runSnippet('getResourceField', $params); break;
                      // А вот сюда потом добавлять новые методы
                    }
                    
                    // Если у нас есть, что отдать на запрос - отдаем и прерываем работу парсера MODX
                    if (!empty($res)) {
                      die($res);
                    }

                    Я создал отдельно ресурс в корне контекста, под названием ajax и соответствующим псевдонимом, он доступен по адресу mysite.ru/ajax.html, содержащий:
                    [[!ajax]]

                    Вероятно чтобы «не путался под ногами» вызов сниппета и централизовать адрес ajax запросов. На всякий случай задал пустой шаблон и, само собой, скрыл для меню.

                    Код в теге HEAD используемого для примера шаблона:
                    <script type="text/javascript" language="JavaScript" src="assets/js/jquery/main.js"></script>
                    <script type="text/javascript" language="JavaScript">
                    jQuery(function($){
                    	$('#results').load('ajax.html', {action: 'field', id: '56', field: 'content'}, function(){
                        		alert('Запрос успешно выполнен! Данные получены.');
                    	});
                    });
                    </script>

                    Код в теге BODY, в любом удобном месте, используемого для примера шаблона:
                    <div id="results"></div>

                    При загрузке страницы с заданным шаблоном, используемым для примера, в тег div, под идентификатором results, произведётся асинхронная загрузка содержимого поля content ресурса с ID 56.
                    1. Антон ХайЭксель 26 february 2013, 00:32 # 0
                      Реализую похожий метод +history.js. Хотелось бы знать, как обращаться к странице чтобы при аякс запросе выдавался пустой шаблон, а при обычном соответственно «непустой»
                      1. Василий Наумкин 26 february 2013, 06:21 # 0
                        Если только плагином ловить и проверять HTTP_X_REQUESTED_WITH, а потом пробовать динамически менять шаблон.

                        Я в последних разработках обращаюсь к специальному файлу, который запускается в MODX_API_MODE, но там есть свои заморочки.
                      2. Антон ХайЭксель 26 february 2013, 07:53 # 0
                        Вообще у меня мысль сводилась к вставке [[!Ajax_test]] в начало шаблона.
                        соответственно при аякс-обращении сначала выполнятся будет сниппет и либо переключать переменную шаблона (в этом месте у меня зациклился мозг), либо выдавать [[*content]]
                        $modx->resource->_content
                        и завершать работу парсера, но тут тоже нерабочий быдлокод вышел в итоге =(

                        в эту сторону можно как то двигаться?
                        1. Василий Наумкин 26 february 2013, 08:07 # 0
                          1. Старайся отвечать на комментарий, а не создавать новую ветку.

                          2. При твоём варианте, шаблон уже загружается, значит ты ничего не выигрываешь. Поэтому, либо плагин, либо файл.

                          Ну, либо делай как в заметке и не парься. Там работа обрывается командой die() — и ничего кроме ответа на Ajax не выводится.
                          1. Иван Брежнев 01 march 2013, 00:45 # 0
                            Я вот писал небольшой плугин для смены шаблона ресурса на лету, там есть описание как сделать чтобы при аякс запросе ресурсу назначался пустой шаблон modx.im/blog/triks/391.html
                            1. Антон ХайЭксель 01 march 2013, 00:51 # 0
                              Спасибо, простенько, но именно то что нужно ;)
                          2. LinkLib LinkLib 28 february 2013, 22:53 # 0
                            Уважаемый Василий! Огромное спасибо за статью.
                            Со всем вроде разобрался, но, как водится, нашел куда упереться — прошу помощи:

                            При активации функции (ссылка class=«ajax_link»), возвращается список ссылок class=«ajax_link».
                            А они работать никак не хотят:( То есть все ссылки, которые были в момент первой загрузке страницы — работают, а новые — не хотят.

                            Не могли бы подсказать, что это может быть за болячка?

                            Заранее благодарен,
                            Дмитрий.
                            1. Василий Наумкин 28 february 2013, 23:13 # 0
                              Заметка написана давно и там не учтены кое-какие вещи. Например то, что функция click() вешается только один раз на все подходящие элементы — при загрузке страницы.

                              После появления, кажется jQuery 1.7 нужно использовать функцию on():
                              $(document).on('click', 'a.ajax_link', function(e) {
                              	var action = $(this).data('action');
                              	$.post(document.location.href, {action: action}, function(data) {
                              		alert('Запрос успешно выполнен');
                              		$('#result').html(data);
                              	})
                              	e.preventDefault();
                              })
                              При таком обработчике все ссылки будут проходить через него, независимо от их происхождения.
                              1. Иван Брежнев 01 march 2013, 01:16 # 0
                                Кстати по поводу e.preventDefault(); есть такой момент, рекомендуют его в начале функции писать, т.к. если вдруг в скрипте во время выполнения всплывет ошибка, перехода по ссылке не будет, т.к. стандартное поведение мы уже сбросили.
                                Т.е. вот так
                                $(document).on('click', 'a.ajax_link', function(e) {
                                	e.preventDefault();
                                	var action = $(this).data('action');
                                	$.post(document.location.href, {action: action}, function(data) {
                                		alert('Запрос успешно выполнен');
                                		$('#result').html(data);
                                	})
                                })
                                Но так же можно использовать return false; в конце функции, потому что e.preventDefault(); не во всех браузерах правильно срабатывает
                                1. Алексей Карташов 08 march 2013, 00:35 # 0
                                  Не, return false это такая коварная штука, которая помимо предотвращения действия по умолчанию, ещё и останавливает «всплытие» события по DOM. А иногда это бывает ой как не хорошо.
                                  Лучше уж сферическое «e.preventDefault(); не во всех браузерах правильно срабатывает», чем долгий и нудный поиск бага на тему — почему же закрывается js-модальное окно или кастомный комбобокс…

                                  Кстати, лично вы встречали какие-либо проблемы при использовании e.preventDefault()? Я лично нет (даже в ишаках, включая 7й), хотя верстаю много и (возможно) профессионально.
                                  1. Иван Брежнев 08 march 2013, 00:44 # 0
                                    Согласен с вами, я не пользуюсь return false.
                                    Вот в этой статье об это подробно рассказано bolsterweb.com/2011/01/javascript-e-preventdefault-is-not-return-false/
                                    1. Алексей Карташов 08 march 2013, 02:49 # 0
                                      Да об этом много где написано, но сам я это не усвоил, пока не потратил несколько часов на устранение описанных выше багов :-)
                                    2. Алексей Карташов 08 march 2013, 02:44 # 0
                                      Ошибочка вышла в комментарии…
                                      Правильно надо так:
                                      поиск бага на тему — почему же НЕ закрывается js-модальное окно или кастомный комбобокс
                              2. LinkLib LinkLib 01 march 2013, 08:25 # 0
                                Василий, Иван, огромное спасибо за помощь!
                                Заработало:)
                                1. Алексей Хребтов 01 march 2013, 16:22 # 0
                                  Всем добрый день!

                                  Василий подскажи, а как можно реализовать в твоем сниппете (в статье), чтобы сработало перенаправление на другую страницу, по типу:
                                  case 'sendData':
                                  $res = $component->sendData($param);
                                  $url = $this->modx->makeUrl(104);

                                  return   $this->modx->sendRedirect($url);
                                  break;
                                  у меня в итого приходит в страницу через ajax вызов страницы с ID = 1.
                                  Голову сломал как оборвать работу ajax сниппета и перенаправить пользователя на страницу.
                                  Как такое можно сделать?
                                  Я могу повесить на success методе в ajax window.location на страницу нужную, но хочу серверное решение.
                                  1. Василий Наумкин 01 march 2013, 16:26 # 0
                                    Прочитай еще раз, что такое Ajax.

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

                                    Отсюда вывод — твоя задача решается только через javascript.

                                    Но не нужно расстраиваться, ведь любое перенаправление юзера — это просто команда браузеру перейти на другу страницу. Что через javascript, что через заголовки от php.

                                    То есть, тут вообще нет «серверного решения», если вдуматься.
                                    1. Алексей Хребтов 01 march 2013, 16:34 # 0
                                      Василий спасибо за ответ!
                                      Прочитал, да все верно говоришь. Тогда не буду извращаться больше, сделаю через window.location.

                                  2. Александр Адеев 06 march 2013, 10:52 # 0
                                    Василий, подскажите, есть 2 вопроса:

                                    1. вызываю Ditto — вывод есть, но в шаблоне есть конструкция с использованием сниппета If и соответственно его вызов не отрабатывается, как это полечить?

                                    2. Фильтрация реализована через сниппет, который обрабатывает url (catalog.html/?f=man) и формирует строку для параметра &filter. Соответственно фильтрация тоже не работает.

                                    есть ли решение данных проблем (особенностей)

                                    заранее спс.
                                    1. Василий Наумкин 06 march 2013, 12:52 # 0
                                      Я уже слабо помню Evolution, поэтому только в общих чертах:

                                      1. Нужно самостоятельно пропарсить результат, в Revo это делается так:
                                      $modx->getParser()->processElementTags('', $html, false, false, '[[', ']]', array(), 10);
                                      $modx->getParser()->processElementTags('', $html, true, true, '[[', ']]', array(), 10);
                                      return $html;
                                      

                                      2. Насколько я понимаю, обрабатывается не url, а параметры в url, переданные в $_GET. Соответственнл, нужно немного изменить сниппет, чтобы он понимал параметры не только оттуда.

                                      Советую задать эти вопросы камрадам с modx.im
                                      1. Александр Адеев 06 march 2013, 13:38 # 0
                                        2. Да, фильтрацию сделал, передав параметр в сниппет — сформировал фильтр.

                                        1. Буду смотреть в сторону парсинга…
                                        1. Андрей Андреев 22 june 2013, 17:58 # 0
                                          В ево это делается с помощью $modx->parseDocumentSource($result);
                                          Т.е как у меня полчилось:
                                          Снипет ajax
                                          if ($_SERVER[HTTP_X_REQUESTED_WITH] == 'XMLHttpRequest')
                                          {
                                          	....
                                          	$result= $modx->runSnippet('Ditto', $param);
                                          	$result = $modx->parseDocumentSource($result);
                                          	die($result);
                                          	
                                          }
                                          
                                          Без $result = $modx->parseDocumentSource($result); у меня внутри чанка Ditto (&tpl) выводило неотпрарсенные сниппеты, [+id+] и тд.
                                          А так всё работает) Я счастлив) В принципе теперь у меня почти достаточно знаний что-бы делать полноценные ajax сайты на Evo.
                                          Нарыл кстати это тут www.liveinternet.ru/journalshowcomments.php?jpostid=223127750&journalid=1999074&go=next&categ=0
                                          1. Андрей Андреев 22 june 2013, 19:45 # 0
                                            Столкнулся с проблемой что не парсит ссылки т.е выводить таким образом — moisite.com/[~57~], решил проблему добавлением функции $modx->rewriteUrls т.е вышло вместо
                                            $result = $modx->parseDocumentSource($result);
                                            это
                                            $result = $modx->rewriteUrls($modx->parseDocumentSource($result));
                                      2. Андрей Андреев 02 june 2013, 16:35 # 0
                                        Блин реально клёвая идея с прерыванием.
                                        Я 3й раз натыкаюсь на эту статью и только сейчас дошла основная идея! Гениально!
                                        Я относительно зёлёный в этих вопросах, тяжко вникать в чужой код, Вы случайно не помните по этому-же принципу работаю ajaxserach в эво и shopkeeper или там сама идея реализации ajax другая?
                                        1. Василий Наумкин 02 june 2013, 17:41 # 0
                                          У Эво, вроде специальный файл для ajaxSearch в корне лежит — index-ajax.php

                                          Ну а про shopkeeper ничего не скажу — не знаю.
                                        2. Pavel Smirnov 02 june 2013, 18:18 # 0
                                          Добрый день! Второй день пытаюсь понять как выводить страницы без перезагрузки. Т.е. что бы кликая по меню перезагружался контент??? Подскажите пожалуйста
                                          1. Володя 13 july 2013, 10:01 # 0
                                            Доброе утро! Пробую разобраться с ajax подзагрузкой ресурсов.
                                            сама страница с вызовом снипета:
                                            <script type="text/javascript" language="JavaScript">
                                            jQuery(function($){
                                                    $('#results').load('[[~43]]', {
                                                        action: 'field33',
                                                        parents: '3',
                                                        limit: '3',
                                                        offset: '3'
                                                        //where: '{"Data.favorite":"1"}'
                                                         });
                                            });
                                            </script>
                                                       <div id="results"></div>  
                                            
                                            43 ресурс там вызов снипета, вот его код:
                                            <?php
                                            if ($_SERVER['HTTP_X_REQUESTED_WITH'] != 'XMLHttpRequest') {return;}
                                            $action = $_POST['action'];
                                            if (empty($action)) {return;}
                                            $res = '';
                                            switch ($action) {
                                                  case 'field33':     
                                                    $param = array(
                                                    'element' => 'msProducts',
                                                    'offset' => (int)$_POST['offset'],
                                                    'limit' => (int)$_POST['limit'],
                                                    'depth' => $depth,
                                                    'parents' => (int)$_POST['parent'],
                                                    'includeContent'=> $includeContent,
                                                    'tpl'=> $tpl,
                                                    'outputSeparator'=> $outputSeparator,
                                                    'showUnpublished'=> $showUnpublished,
                                                    'showDeleted'=> $showDeleted,
                                                    'showHidden'=> $showHidden
                                                     );
                                                     $documents = $modx->runSnippet('getPageExt',$param);
                                                     return $documents;
                                            }
                                            вопрос — при таком вызове у меня не работает параметр offset, как мне сделать смещение для обновления страницы при пагинации?
                                            спасибо!
                                            1. Володя 13 july 2013, 10:48 # 0
                                              я правильно понимаю что getpage сам отслеживает номер страницы? Мне нужно поймать при нажатии пагинации page=число и передать в снипет?
                                              1. Володя 13 july 2013, 11:06 # 0
                                                неа не пашет((( и если задать 'page' => номер страницы — тоже не пашет…
                                                1. Володя 13 july 2013, 11:31 # 0
                                                  все разобрался — надо просто ловить ссылку с пагинации и передавать в снипет. все пашет!
                                                  1. ну рас разобрался оставил бы решение тут, для потомков))
                                                    1. Володя 14 july 2013, 01:38 # 0
                                                      да пожалуйста, на правильность не претендую — с ajax только знакомиться начал.
                                                      $(document).on('click', 'a.pagin', function(e) {
                                                      	e.preventDefault();
                                                      	var url = $(this).attr('href').replace("get?page=","");
                                                      	jQuery(function($){
                                                              $('#results').load('[[~43]]', {
                                                                  action: 'field33',
                                                                  parents: '3',
                                                                  page: url,
                                                                  limit: '10',
                                                                 });
                                                      }); 
                                                2. Василий Наумкин 13 july 2013, 11:30 # 0
                                                  А у меня пашет.

                                                  Смотри, как работает пагинация в фильтре тут.
                                              2. Елена 08 august 2013, 14:09 # 0
                                                Василий, подскажите пожалуйста, как передать данные на сервер? Желательно через POST, или же любым другим способом… В контексте Ajax, разумеется)
                                                1. Василий Наумкин 08 august 2013, 14:26 # 0
                                                  А заметка про что?
                                                  1. Николай 08 august 2013, 19:24 # 0
                                                    Человек наверно хочет узнать как ему передать из
                                                    <input type = "test" name = "email" />
                                                    Мне было бы тоже интересно узнать…
                                                2. Тарас Малый 27 september 2013, 13:28 # 0
                                                  По данной аналогии все работает и выводится в контейнер. А как сделать что бы вывод делать не в контейнер, а в модальное окно? Например в fancyBox.
                                                  1. Сергей Шлоков 27 september 2013, 16:44 # 0
                                                    Забавный вопрос. Заполняйте контейнер (div), который выводит fancybox и все.
                                                  2. Сергей Шлоков 30 september 2013, 17:37 # 0
                                                    Василий, подскажи, пожалуйста, почему в блоке $.post не переопределяется переменная
                                                    $(document).ready(function() {
                                                         $('#btnSave').click(function() {
                                                                var action = $(this).data('action');
                                                                var str='Test'   //Определили переменную var
                                                                $.post(document.location.href, {action: action}, function(data) {
                                                    		alert(str)  //Выводит "Test"
                                                                    str='OK'  // Переопределили переменную
                                                                })
                                                                alert(str)  //Выводит "Test"
                                                                return false;
                                                            })
                                                    
                                                    Какая то загадка. Полдня уже мучаюсь.
                                                    1. Василий Наумкин 30 september 2013, 18:51 # 0
                                                      Потому что запрос асинхронный.
                                                      Пока он отправляется на сервер, скрипт работает дальше, и переменная переопределится уже после второго алерта, когда придёт ответ.

                                                      Нужно или засунуть весь код, работающий с переменной в каллбэк, или делать синхронный запрос (не советую).
                                                      1. Сергей Шлоков 30 september 2013, 22:37 # 0
                                                        Ясно. А как правильно проверить возвращаемые данные? Такая конструкция
                                                        (data != "")
                                                        работает криво. Я проверяю форму на ошибки. Если они есть, то возвращаю ошибку и вывожу в div #Error.
                                                        $.post(document.location.href, {action: action}, function(data) {
                                                        if (data != "") {
                                                        $("#Error").html(data);
                                                        })
                                                        
                                                        Если ошибки нет, то возвращаю пустую строку ($res=''). И вот тут _опа. В контейнер
                                                        <div id="Error"></div>
                                                        почему-то пишется вся страница:
                                                        <html>
                                                        <head>
                                                        ...
                                                        </head>
                                                        и т.д.
                                                        
                                                        Подскажи, как правильно вернуть пустое значение.
                                                        1. Сергей Шлоков 01 october 2013, 07:28 # 0
                                                          Понял почему. Перед die('') делал редирект. Вот дурень. Правда теперь обновление страницы пришлось делать javascript'ом, ибо не соображу как сделать серверный refresh при закрытии модального окна.
                                                      2. Сергей Шлоков 10 october 2013, 11:37 # 0
                                                        Василий, подскажи, как поменять $_SESSION переменную через post запрос? Я делаю так:
                                                        Инициализирую переменную:
                                                        $_SESSION['var'] = 1;
                                                        При клике на ссылку посылаю ajax
                                                        $.post(document.location.href, {action: 'changevar', id: 5})
                                                        В сниппете меняю переменную
                                                        $_SESSION['var'] = $_POST['id']; // $_POST['id']=5
                                                        Проверяю, $_SESSION['var'] — все равно равна 1. Не понимаю, это же глобальный массив.
                                                        Если в сниппете поставить
                                                        $_SESSION['var'] = $_POST['id']; // $_POST['id']=5
                                                        die($_SESSION['var'])
                                                        Ajax post возвращает 5. Но $_SESSION['var'] не меняется.
                                                        Видимо есть какое-то кодовое слово, чтобы заставить $_SESSION['var'] измениться.
                                                        1. Дмитрий Гречко 10 october 2013, 12:25 # 0
                                                          нет такого слова. Проверяйте код. Должно все работать.
                                                          1. Сергей Шлоков 10 october 2013, 16:52 # 0
                                                            И я так понимаю, что должно. Но увы… Вот код для проверки:
                                                            <?php
                                                            define('MODX_API_MODE', true);
                                                            require 'index.php';
                                                            $modx->getService('error','error.modError');
                                                            $modx->setLogLevel(modX::LOG_LEVEL_INFO);
                                                            $modx->setLogTarget(XPDO_CLI_MODE ? 'ECHO' : 'HTML');
                                                            ?>
                                                            <!DOCTYPE html>
                                                            <head>
                                                                <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js" type="text/javascript"></script>
                                                                <script type="text/javascript">
                                                                    $(document).ready(function() {
                                                                        $('a').click(function() {
                                                                            $.post(document.location.href, {action: 'change', val: '5'},function(data) {
                                                                                $('#res').html(data);
                                                                            })
                                                                            return false;
                                                                        })
                                                                    })
                                                                </script>
                                                            </head>
                                                            <body>
                                                            <?php
                                                            //')
                                                            if ($_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') {
                                                                if ($_POST['action'] == 'change') {
                                                                    $_SESSION['testvar'] = $_POST['val'];
                                                                    die('
                                                            Новое значение $_SESSION["testvar"]:  '.$_SESSION["testvar"].'.  (А теперь нажимаем F5 и видим старое значение.)');
                                                                }
                                                            }
                                                            if (!isset($_SESSION['testvar'])) {$_SESSION['testvar'] = 1;}
                                                            echo "<a id=\"test\" href=\"#1\">Изменить значение</a>";
                                                            echo "<div id=\"res\">
                                                            \$_SESSION[\"testvar\"]:  {$_SESSION['testvar']}</div>";
                                                            ?>
                                                            </body>
                                                            
                                                            Видимо, есть какая-то тонкость, которая ускользает от моего неопытного взгляда.
                                                            1. Василий Наумкин 10 october 2013, 17:02 # 0
                                                              Скопипастил твой код в файл, положил его в корень сайта, открыл — работает.

                                                              Ругается только на
                                                              PHP notice: Undefined index: HTTP_X_REQUESTED_WITH


                                                              У тебя, может, какой-то php-apc работать не дает? Попробуй добавить перед die
                                                              @session_write_close();
                                                              1. Сергей Шлоков 10 october 2013, 17:21 # 0
                                                                С session_write_close() работает. Супер. Грасиас мерси.
                                                                П.С. Я на Timeweb разрабатываю, значит у них чего-то перекручено.
                                                                П.П.С. Еще вопросик по безопасности сессий — злоумышленник может подменить значение $_SESSION переменной?
                                                                1. Василий Наумкин 10 october 2013, 17:26 # 0
                                                                  Если только залезет в базу данных MODX — все сессии хранятся там. В случае других CMS они в файлах на сервере.

                                                                  Насколько я знаю, сессии абсолютно безопасны, но их можно увести, узнав идентификатор, например через XSS на сайте.
                                                                  Тут уже лучше Евгения расспрашивать.
                                                        2. Дмитрий Литвиненко 04 november 2013, 14:21 # 0
                                                          Добрый день, Василий.
                                                          Очень удобное решение, работает отлично!
                                                          Решил организовать загрузку ресурсов (целиком) в целевой #content следующим образом:

                                                          ajax.php (перехватывает id запрашиваемого документа из атрибута ссылки «data-rid»):
                                                          <?php
                                                          if (!defined('MODX_API_MODE')) {
                                                              define('MODX_API_MODE', true);
                                                          }
                                                          @include(dirname(__FILE__) . '/config.core.php');
                                                          if (!defined('MODX_CORE_PATH')) define('MODX_CORE_PATH', dirname(__FILE__) . '/core/');
                                                          @include_once (MODX_CORE_PATH . "model/modx/modx.class.php");
                                                          $modx= new modX();
                                                          
                                                          if ($_SERVER['HTTP_X_REQUESTED_WITH'] != 'XMLHttpRequest') {return;}
                                                          
                                                          $action = filter_input(INPUT_POST,'action');
                                                          $output = '';
                                                          if (empty($action)) {return;}
                                                          
                                                          switch ($action) {
                                                              case 'getResources':
                                                                  $id= filter_input(INPUT_POST,'rid');
                                                                  $resource = $modx->getObject('modResource',array('id' => $id));
                                                                  $pagetitle = $resource->get('pagetitle');
                                                                  $content = $resource->get('content');
                                                                  echo "<h1>$pagetitle</h1>";
                                                                  echo "$content";
                                                                  break;
                                                          
                                                          }
                                                          if (!empty($output)) {
                                                              die($output);
                                                          }
                                                          ?>
                                                          
                                                          JS-обработчик:
                                                          $('a.ajax').click(function() {
                                                              var data = $(this).data();
                                                          
                                                              var urlPath = $(this).attr('href');
                                                              var title = $(this).text();
                                                              var rid = $(this).data('rid');
                                                          
                                                                $.ajax({
                                                                  type: "POST",
                                                                  url: "/ajax.php",
                                                                  data: data,
                                                                  success: function(data) {
                                                                    document.title = title;
                                                                    history.pushState('', document.title, urlPath);
                                                                    showContent(data);
                                                                  }
                                                                })
                                                          
                                                              function showContent(data) {
                                                                      $('#content').html(data);
                                                              }
                                                              return false;
                                                            })
                                                          Но возник вопрос с работой кнопки истории Назад. Не подскажите в каком направлении разумнее всего копать? (пробовал history.js, не до конца понял как связать все в одиное целое)
                                                          1. Василий Наумкин 04 november 2013, 16:31 # 0
                                                            Не знаю, не делал так.
                                                          2. Андрей Копп 26 march 2015, 17:51 # 0
                                                            Дальше нужно набивать новые методы в сниппет (вызов wayfinder, например), расставлять ссылки по страницам и развешивать обработчики событий на них.
                                                            А можно пример для Wayfinder'a? Хочется сделать сайт с переходами по ссылкам через AJAX.
                                                            1. Борис И 02 april 2016, 08:36 # 0
                                                              Василий, доброе утро. Прошу подсказать.
                                                              Пытаюсь вывести через ajax сниппет (т.е изначально сниппета на странице нет, он выводится по срабатыванию события). Сниппет выводится, но текстом, парсер видимо его не обрабатывает.
                                                              Если меняю в коде
                                                              die($res); на echo($res)
                                                              то выводится целая страница в странице, вместе с обработанным сниппетом.
                                                              1. Если я правильно понял, то парсер заново обрабатывает всю страницу, поэтому и получается страница в странице?
                                                              2. Возможно ли сделать так, чтобы парсер обрабатывал только вновь вставленный сниппет?
                                                              1. Максим Кузнецов 02 april 2016, 08:47 # 0
                                                                У вас, видимо, включен fenom? Тогда скорее всего, причина в этом.

                                                                Для решения вы можете вынести код сниппета в отдельный файл (ajax/name.php):

                                                                <?php
                                                                	if ($_SERVER['HTTP_X_REQUESTED_WITH'] != 'XMLHttpRequest' || empty($_POST['action'])) {
                                                                		return;
                                                                	}
                                                                
                                                                	define('MODX_API_MODE', true);
                                                                
                                                                	require '/путь_до_index.php';
                                                                	
                                                                	// Запускает нужные службы MODX
                                                                	$modx->getService('error','error.modError');
                                                                	$modx->setLogLevel(modX::LOG_LEVEL_ERROR);
                                                                	$modx->setLogTarget(XPDO_CLI_MODE ? 'ECHO' : 'HTML');
                                                                
                                                                
                                                                	//ваш код сниппета который, в итоге, формирует $result
                                                                
                                                                
                                                                	if (!empty($result)) {
                                                                		die(json_encode($result));
                                                                	}
                                                                

                                                                И при помощи jQuery на срабатывания нужного события запрашивать/возвращать результат:
                                                                $.ajax({
                                                                	type: "POST",
                                                                	url: 'ajax/name.php',
                                                                	data: {отправляемые данные},
                                                                					
                                                                	success: function(response) {
                                                                		var data = eval(response);
                                                                
                                                                		//вставляете в нужную область результат работы сниппета
                                                                	}
                                                                });
                                                                1. Борис И 02 april 2016, 08:58 # 0
                                                                  Fenom, не использую. Пытаюсь вывести сниппет TicketForm с разными parent в зависимости от выбранного события
                                                                  1. Борис И 02 april 2016, 09:20 # 0
                                                                    Код сниgпета Ajax1
                                                                    <?php
                                                                    // Откликаться будет ТОЛЬКО на ajax запросы
                                                                    if ($_SERVER['HTTP_X_REQUESTED_WITH'] != 'XMLHttpRequest') {return;}
                                                                    
                                                                    // Сниппет будет обрабатывать не один вид запросов, поэтому работать будем по запрашиваемому действию
                                                                    // Если в массиве POST нет действия - выход
                                                                    if (empty($_POST['action'])) {return;}
                                                                    $action1= $_POST['action'];
                                                                    $res = "[[!TicketForm? &requiredFields=`longtitle,model,introtext` &allowedFields=`longtitle,fullname,model,introtext,avtor,video,yaobzor` &redirectUnpublished=`63` &parents=`$action1` &tplFormCreate=`tpl.Tickets.form.create1` ]]";
                                                                    // Если у нас есть, что отдать на запрос - отдаем и прерываем работу парсера MODX
                                                                    if (!empty($res)) {
                                                                    echo($res);
                                                                    }
                                                                    И скрипт с частью страницы, помещен в чанк ajaxscr, на странице выводится через чанк
                                                                    <div class="well create">
                                                                        <form name="ticketFormcat" action="" id="ticketFormcat" method="post" >
                                                                            <div class="form-group">
                                                                    	    	<label  for="ticket-sections">Бытовая техника <span style="color: #F00;font-size: 12px;}">(выберите в первую очередь)</span></label>
                                                                                    <select name="parentcat" class="form-control" id="parentcat" onchange="viborid(this.value);">
                                                                                           <option disabled="disabled" value="" ></option>
                                                                                            [[pdoMenu?
                                                                                            &parents=`0`
                                                                                            &level=`1`
                                                                                            &firstClass=``
                                                                                            &lastClass=``
                                                                                            &hereClass=``
                                                                                            &outerClass=``
                                                                                            &tplOuter=`@INLINE [[+wrapper]]`
                                                                                            &tpl=`@INLINE <option value="[[+id]]">[[+menutitle]]</option>`
                                                                                        ]]
                                                                                </select>
                                                                            </div>
                                                                        </form>
                                                                        
                                                                        
                                                                        
                                                                        
                                                                       <script type="text/javascript">
                                                                       function viborid(action) {
                                                                       
                                                                      // Ajax запрос к текущей страницы (а на ней наш сниппет) методом post
                                                                    	$.post(document.location.href, {action: action}, function(data) {
                                                                    		// Выдаем ответ
                                                                    		alert('Запрос успешно выполнен')
                                                                    		$('#result').html(data);
                                                                    	})
                                                                      
                                                                       }
                                                                       
                                                                    </script>
                                                                    
                                                                    <!-- html элемент для вставки ответа от php -->
                                                                    <div id="result"></div>
                                                                    </div>
                                                                    
                                                                    Вывод на странице
                                                                    [[!Ajax1]]
                                                                    [[$ajaxscr?]]
                                                                    1. Максим Кузнецов 02 april 2016, 10:33 # +1
                                                                      $res = "[[!TicketForm? &requiredFields=`longtitle,model,introtext` &allowedFields=`longtitle,fullname,model,introtext,avtor,video,yaobzor` &redirectUnpublished=`63` &parents=`$action1` &tplFormCreate=`tpl.Tickets.form.create1` ]]";
                                                                      — это определенно лучше переписать:

                                                                      $params = array();
                                                                      $params['requiredFields'] = 'longtitle,model,introtext';
                                                                      $params['parents'] = $action1;
                                                                      //...
                                                                      
                                                                      $res = $modx->runSnippet('TicketForm', $params);
                                                                      
                                                                      if (!empty($res)) {
                                                                      	die($res);
                                                                      }
                                                                      
                                                                      1. Борис И 02 april 2016, 11:00 # 0
                                                                        Вызов стал таким
                                                                        $params = array();
                                                                        $params['requiredFields'] = 'longtitle,model,introtext';
                                                                        $params['parents'] = $action1;
                                                                        $params['allowedFields'] = 'longtitle,fullname,model,introtext,avtor,video,yaobzor';
                                                                        $params['redirectUnpublished'] = '63';
                                                                        $params['tplFormCreate'] = 'tpl.Tickets.form.create1';
                                                                        
                                                                        $res = $modx->runSnippet('TicketForm', $params);
                                                                        Спасибо страница перестала дублироваться, но обратил внимание на другую проблему.

                                                                        Форма TicketForm не работает, ничего не отправляет, не добавляет фото (при клике ничего не происходит), рядом с «Выберите файл» появились «0/0 0%», при нажатии кнопки «на проверку» (даже с пустой формой) просто перезагружается страница
                                                                        1. Борис И 02 april 2016, 11:07 # 0
                                                                          При этом если на этой же странице вывести форму обычно
                                                                          [[!TicketForm? 
                                                                          &requiredFields=`longtitle,model,introtext` 
                                                                          &allowedFields=`longtitle,fullname,model,introtext,avtor,video,yaobzor` 
                                                                          &redirectUnpublished=`63` 
                                                                          &parents=`0` 
                                                                          &tplFormCreate=`tpl.Tickets.form.create1` ]]
                                                                          то данная форма работает.
                                                                          Форма выведенная через ajax не работает
                                                                          1. Максим Кузнецов 02 april 2016, 11:11 # +1
                                                                            Это потому что TicketForm при инициализации подключает свои скрипты на страницу.
                                                                            В случае же с аякс-загрузкой их подключение не происходит.
                                                                            1. Борис И 02 april 2016, 11:41 # 0
                                                                              Попробовал подключить вручную, ситуация не изменилась.
                                                                              Подумал, что что-то пропустил и вывел на эту же страницу [[!TicketForm?]] для подключения скриптов, ситуация не изменилась. Стандартная форма работает, форма через ajax нет. Еще обратил внимание, что у формы через ajax нет (не выведены) элементов форматирования текста: B,I…
                                                                              1. Воеводский Михаил 02 april 2016, 17:31 # 0
                                                                                Далеко не все динамически добавленные на страницу элементы корректно обрабатываются скриптами на js. Это особенность самого js.
                                                                              2. Борис И 02 april 2016, 12:25 # 0
                                                                                Все переделал еще раз и часть функций, при наличии стандартного сниппета [[!TicketForm?]] на странице, заработало, а вот добавление файлов нет и элементы форматирования текста: B,I… не появились.
                                                                                1. Борис И 02 april 2016, 12:52 # 0
                                                                                  Где то еще подключаются скрипты.
                                                                                  Формы отличаются
                                                                                  1. Форма «Содержимое» через ajax
                                                                                  <div class="form-group">
                                                                                  		<textarea class="form-control" placeholder="Содержимое" name="content" id="ticket-editor" rows="10"></textarea>
                                                                                  		<span class="error"></span>
                                                                                  	</div>
                                                                                  2. «Форма Содержимое» стандартное
                                                                                  <div class="form-group">
                                                                                  		<div><div id="markItUpTicket-editor" class="markItUp"><div class="markItUpContainer"><div class="markItUpHeader"><ul><li class="markItUpButton markItUpButton1 btn-bold"><a href="" accesskey="B" title="Выделить жирным [Ctrl+B]">Выделить жирным</a></li><li class="markItUpButton markItUpButton2 btn-italic"><a href="" accesskey="I" title="Курсив [Ctrl+I]">Курсив</a></li><li class="markItUpButton markItUpButton3 btn-underline"><a href="" accesskey="U" title="Подчеркивание [Ctrl+U]">Подчеркивание</a></li><li class="markItUpButton markItUpButton4 btn-stroke"><a href="" accesskey="H" title="Выделение подзаголовков [Ctrl+H]">Выделение подзаголовков</a></li><li class="markItUpSeparator">---------------</li><li class="markItUpButton markItUpButton5 btn-bulleted"><a href="" title="Ненумерованный список">Ненумерованный список</a></li><li class="markItUpButton markItUpButton6 btn-numeric"><a href="" title="Нумерованный список">Нумерованный список</a></li><li class="markItUpSeparator">---------------</li><li class="markItUpButton markItUpButton7 btn-quote"><a href="" title="Цитата">Цитата</a></li><li class="markItUpButton markItUpButton8 btn-link"><a href="" title="Link">Link</a></li><li class="markItUpButton markItUpButton9 btn-picture"><a href="" title="Картинка">Картинка</a></li></ul></div><textarea class="form-control markItUpEditor" placeholder="Содержимое" name="content" id="ticket-editor" rows="10"></textarea><div class="markItUpFooter"><div class="markItUpResizeHandle"></div></div></div></div></div>
                                                                                  		<span class="error"></span>
                                                                                  	</div>
                                                                                  Но даже если все это добавить в форму выводимую через ajax, кнопки появляются, но не работают. Где-то внутри еще есть подключение скриптов.
                                                                                  Я защел в тупик, куда копать дальше
                                                                                  1. Максим Кузнецов 02 april 2016, 17:07 # 0
                                                                                    Конкретно TicketForm подключают следующие скрипты:

                                                                                    /assets/components/tickets/js/web/default.js
                                                                                    /assets/components/tickets/js/web/lib/plupload/plupload.full.min.js
                                                                                    /assets/components/tickets/js/web/files.js
                                                                                    + формируют массив TicketsConfig.
                                                                                    + (опционально) скрипты редактора MarkitUp.

                                                                                    Вы можете попробовать подгружать их самостоятельно и запускать их внутренние элементы инициализации через $(document).ajaxStop. Плюс формировать конфигурационный массив самостоятельно.

                                                                                    UPD: Но я вас заверяю — без глубоких знаний php+js оно того вообще не стоит и грозит очень серьезно переписать исходный сниппет.
                                                                                    1. Борис И 02 april 2016, 17:36 # 0
                                                                                      Спасибо за помощь, буду понемногу вникать в php+js
                                                                                      1. Николай Савин 03 april 2016, 19:22 # 0
                                                                                        Не проще вызвать сниппет на странице, который в свою очередь подключит все необходимые скрипты, а форму если она не нужна скрыть. Потом при необходимости показывать ее через js
                                                                                        1. Борис И 03 april 2016, 19:42 # 0
                                                                                          Так пробовал делать, часть функций все равно не работает, как писал выше
                                                                        You need to login to create comments.