Сразу должен заметить, что полноценной библиотекой это назвать пока что нельзя, но на практике использовать вполне можно. Например, компонент ввода облака тегов использует новую библиотеку для показа списка похожих тегов при вводе тега.
[spoiler]
Поддержка аякса на уровне JavaScript реализована в этом файле:
/bitrix/js/main/cphttprequest.js
Давайте разберем, как это "добро" можно использовать. Ну например, пусть это будет страница, где требуется без перезагрузки показывать аяксом разные блоки информации. В одном блоке выведем новости, в другом - опрос, в третьем - случайное фото. Все блоки можно реализовать стандартными компонентами.
1. Заготовка страницы.
Создаем новую страницу, и в визуальном редакторе размещаем и настраиваем на ней три компонента (новости, опрос, случайное фото). Получаем что-то такое (параметры компонентов опущены):
<?$APPLICATION->IncludeComponent( "bitrix:news.list", ".default", Array(...) );?> <?$APPLICATION->IncludeComponent( "bitrix:voting.form", "", Array(...) );?> <?$APPLICATION->IncludeComponent( "bitrix:photo.random", "", Array(...) );?> |
2. Пишем логику показа только одного блока из трех. Для этого будем использовать ссылки со специальной переменной show. Сразу после подключения header.php напишем:
$show = $_REQUEST["show"]; if($show <> "vote" && $show <> "photo") $show = "news"; |
Ниже выведем блок ссылок с вариацией параметра show:
<p> <a href="t.php?show=news">Новости</a> | <a href="t.php?show=vote">Опрос</a> | <a href="t.php?show=photo">Случайное фото</a> </p> |
Между вызовами компонентов расставим соответствующие условия:
<?if($show == "news"):?> <?$APPLICATION->IncludeComponent("bitrix:news.list", ...);?> <?elseif($show == "vote"):?> <?$APPLICATION->IncludeComponent("bitrix:voting.form", ...);?> <?elseif($show == "photo"):?> <?$APPLICATION->IncludeComponent("bitrix:photo.random", ...);?> <?endif?> |
Убеждаемся, что все работает при переходе по этим ссылкам.
А вот теперь сделам так, чтобы при клике мышкой на ссылку наш блок загружался аяксом без перезагрузки страницы в окне браузера.
3. Подключим библиотеку аякса после блока определения переменной show:
IncludeAJAX(); |
3.1. Чтобы данный вызов работал правильно, необходимо, чтобы в шаблоне сайта был следующий код:
<?$APPLICATION->ShowHeadStrings()?>
<?$APPLICATION->ShowHeadScripts()?>
Код необходимо разместить между тегами <head> и </head>, т.е. там же, где и <?$APPLICATION->ShowCSS();?>.
4. Поместим все три компонента в div. В него мы будем записывать результат работы аяксового обращения к серверу:
<div id="ajax_result"> <?if($show == "news"):?> <?$APPLICATION->IncludeComponent()?> .... <?endif?> </div> |
5. Самое интересное. Над блоком ссылок определим JavaScript-функцию, которая будет вызываться при клике на ссылке. Собственно, эта функция и есть само воплощение аякса.
<script type="text/javascript"> function GetPage(show) { function ShowResult(data) { var obContainer = document.getElementById('ajax_result'); if (obContainer) obContainer.innerHTML = data; } var TID = CPHttpRequest.InitThread(); CPHttpRequest.SetAction(TID, ShowResult); CPHttpRequest.Send(TID, '/t.php', {'show':show, 'mode':'ajax'}); return false; } </script> |
Обратите внимание на объект CPHttpRequest. Именно он определяется в JS-библиотеке и служит для передачи запроса на сервер. Функция ShowResult() непосредственно вставляет полученный результат запроса в добавленный нами div.
1) инициализируем запрос:
var TID = CPHttpRequest.InitThread();
2) устанавливаем обработчик ответа сервера, передавая нашу функцию ShowResult:
CPHttpRequest.SetAction(TID, ShowResult);
3) отправялем запрос на сервер
CPHttpRequest.Send(TID, '/t.php', {'show':show, 'mode':'ajax'});
Здесь '/t.php' - адрес нашей страницы,
{'show':show, 'mode':'ajax'} - параметры, передаваемые в запросе. На сервер придет запрос GET в виде:
GET /t.php?show=news&mode=ajax
6. Добавляем вызов нашей функции в блок ссылок:
<p> <a href="t.php?show=news" onclick="return GetPage('news');">Новости</a> | <a href="t.php?show=vote" onclick="return GetPage('vote');">Опрос</a> | <a href="t.php?show=photo" onclick="return GetPage('photo');">Случайное фото</a> </p> |
Обратите внимание, что для поисковика это нормальные ссылки, которые ведут на обычные страницы с разным контентом. Для "людей" же, поддерживающих JavaScript, эти ссылки будут работать без перезагрузки страницы.
ПРОБУЕМ, что получилось....
Работает! 8) За исключением того, что в наш div мы вставили страницу целиком, вместе с дизайном. Дизайн в рекурсии, так сказать.
7. Чтобы исключить такое поведение нашей страницы, мы должны в режиме аякса отключать все лишнее, что не должно выводиться. Это и определение нашей JS-функции, и блок ссылок, и дизайн целиком. Именно для этого в аякс-режиме мы передаем переменную mode=ajax.
Добавляем код:
<? if($_REQUEST["mode"] == "ajax"): $APPLICATION->RestartBuffer(); else: // тут весь вывод для "статической" страницы endif; //$_REQUEST["mode"] == "ajax" ?> тут логика страницы для обоих режимов <? if($_REQUEST["mode"] == "ajax") die(); ?> |
8. Вот теперь работает правильно. Но не хватает информативности для пользователя, который нажимает на ссылку и ждет непонятно чего. Добавим сообщение "Загрузка...".
В блоке ссылок добавим контейнер для сообщения:
<p> <a href="t.php?show=news" onclick="return GetPage('news');">Новости</a> | <a href="t.php?show=vote" onclick="return GetPage('vote');">Опрос</a> | <a href="t.php?show=photo" onclick="return GetPage('photo');">Случайное фото</a> <span id="wait_container" style="font-style:italic"></span> </p> |
В нашей функции вызовем метод PShowWaitMessage для показа сообщения перед загрузкой. Метод PCloseWaitMessage вызывается для очистки сообщения после загрузки:
function GetPage(show) { function ShowResult(data) { PCloseWaitMessage('wait_container'); var obContainer = document.getElementById('ajax_result'); if (obContainer) obContainer.innerHTML = data; } PShowWaitMessage('wait_container'); var TID = CPHttpRequest.InitThread(); CPHttpRequest.SetAction(TID, ShowResult); CPHttpRequest.Send(TID, '/t.php', {'show':show, 'mode':'ajax'}); return false; } |
Теперь все это очень похоже на настоящий АЯКС!
Приводим полный листинг нашей тестовой страницы:
<? require($_SERVER["DOCUMENT_ROOT"]."/bitrix/header.php"); $show = $_REQUEST["show"]; if($show <> "vote" && $show <> "photo") $show = "news"; if($_REQUEST["mode"] == "ajax"): $APPLICATION->RestartBuffer(); else: $APPLICATION->SetTitle("Title"); IncludeAJAX(); ?> <script type="text/javascript"> function GetPage(show) { function ShowResult(data) { PCloseWaitMessage('wait_container'); var obContainer = document.getElementById('ajax_result'); if (obContainer) obContainer.innerHTML = data; } PShowWaitMessage('wait_container'); var TID = CPHttpRequest.InitThread(); CPHttpRequest.SetAction(TID, ShowResult); CPHttpRequest.Send(TID, '/t.php', {'show':show, 'mode':'ajax'}); return false; } </script> <p> <a href="t.php?show=news" onclick="return GetPage('news');">Новости</a> | <a href="t.php?show=vote" onclick="return GetPage('vote');">Опрос</a> | <a href="t.php?show=photo" onclick="return GetPage('photo');">Случайное фото</a> <span id="wait_container" style="font-style:italic"></span> </p> <div id="ajax_result"> <? endif; //$_REQUEST["mode"] == "ajax" ?> <?if($show == "news"):?> <?$APPLICATION->IncludeComponent( "bitrix:news.list", ".default", Array( "IBLOCK_TYPE" => "news", "IBLOCK_ID" => "33", "NEWS_COUNT" => "5", "SORT_BY1" => "ACTIVE_FROM", "SORT_ORDER1" => "DESC", "SORT_BY2" => "SORT", "SORT_ORDER2" => "ASC", "FILTER_NAME" => "", "FIELD_CODE" => array(0=>"",1=>"",), "PROPERTY_CODE" => array(0=>"",1=>"",), "DETAIL_URL" => "news_detail.php?ID=#ELEMENT_ID#", "CACHE_TYPE" => "A", "CACHE_TIME" => "3600", "CACHE_FILTER" => "N", "PREVIEW_TRUNCATE_LEN" => "", "ACTIVE_DATE_FORMAT" => "d.m.Y", "DISPLAY_PANEL" => "N", "SET_TITLE" => "Y", "INCLUDE_IBLOCK_INTO_CHAIN" => "Y", "ADD_SECTIONS_CHAIN" => "Y", "HIDE_LINK_WHEN_NO_DETAIL" => "N", "PARENT_SECTION" => "", "DISPLAY_TOP_PAGER" => "N", "DISPLAY_BOTTOM_PAGER" => "N", "PAGER_TITLE" => "Новости", "PAGER_SHOW_ALWAYS" => "Y", "PAGER_TEMPLATE" => "", "PAGER_DESC_NUMBERING" => "N", "PAGER_DESC_NUMBERING_CACHE_TIME" => "36000", "DISPLAY_DATE" => "Y", "DISPLAY_NAME" => "Y", "DISPLAY_PICTURE" => "Y", "DISPLAY_PREVIEW_TEXT" => "Y" ) );?> <?elseif($show == "vote"):?> <?$APPLICATION->IncludeComponent( "bitrix:voting.form", "", Array( "VOTE_ID" => "3", "VOTE_RESULT_TEMPLATE" => "vote_result.php?VOTE_ID=#VOTE_ID#", "CACHE_TYPE" => "A", "CACHE_TIME" => "3600" ) );?> <?elseif($show == "photo"):?> <?$APPLICATION->IncludeComponent( "bitrix:photo.random", "", Array( "IBLOCK_TYPE" => "photo", "IBLOCKS" => Array("8"), "PARENT_SECTION" => "", "DETAIL_URL" => "/gallery/detail.php?SECTION_ID=#SECTION_ID#&ID=#ELEMENT_ID#", "CACHE_TYPE" => "A", "CACHE_TIME" => "180" ) );?> <?endif?> <? if($_REQUEST["mode"] == "ajax") die(); ?> </div> <? require($_SERVER["DOCUMENT_ROOT"]."/bitrix/footer.php"); ?> |
По подобию Вашего примера, скажем, на странице есть 3 компонента и один из них работает через аякс, допустим это будет компонент опросов. Причем, все комопненты должны выводиться по умолчанию в любом режиме, только лишь с той разницей, что у посетителей, с "модными" браузерами, проголосовав будет происходить не полная перезагрузка страницы, а лишь компонент опросов. Ссылка аякс-запроса само-собой ведет на эту же страницу. И чтобы избежать вызовов ненужных компонентов при аякс-запросе единственный выход - это делать так, как Вы показали в примере, т.е. через вставку дополнительного php-кода в саму страницу, что конечно же не приемлемо для дальнейшего использования администраторами сайтов, да и визредактор может где-то что-то не понять, другими словами "не юзабельно" это.
Т.е. в планах изменить логическую часть компонентов или все же объектную модель компонентов?
Например, если работа одного компонента зависит от результата работы другого компонента - то нужно выполнять их оба. Есть еще комплексные компоненты, внутри которых вызываются простые.
Поэтому меня и интересует вопрос, вы планируете просто добавлять во все стандартные компоненты логику определения режима работы компонента, либо же наращивать объектную модель APPLICATION?
На мой взгляд, было бы удобно на уровне APPLICATION уже в прологе (в режиме аякс-запроса) знать какие компоненты нужно выполнять, а какие игнорировать. Ведь, по сути, только через APPLICATION удобно передавать идентификаторы компонентов. Или я ошибаюсь?
На уровне ядра знать нужно обязательно. Тот же дизайн отключить. Или вывод дебаг-информации внизу страницы.
и каптча уж очень долго грузится у вас в блоге
Чего сильно хотелось бы и чего просто невероятно не хватает:
Сейчас каритна мира выглядит так - мы запрашиваем страницу и с помощью костылей обрубаем лишнее. Это логичный, но не совсем верный подход - все же костыль и плохо воспроизводимо. Гораздо эффективнее не отрезать а добавлять - грубо говоря есть страница, которая получает параметр - мнемонический код или ID инфоблока и возвращает его HTML. Можно усовершенствовать процесс и сделать блоки, выводимые через ajax, отдельно настраиваемыми. Ничего военно-морского здесь нет, делается быстро, но было бы хорошо если бы это было в стандартной поставке.
К тому же:
/news.php
/ajax.php
Cтраница более читаема, да и подход на порядок более гибкий.
А если в GetPage() добавить вторым параметром id dom-элемента, в который нужно плюхнуть данные и внести ее в CPHttpRequest - юзвери спасибо скажут.
То бишь в идеале страница получается такой:
С высокой (близкой к 100%) вероятностью это не будет работать в IE. Не можем мы в DOM вставлять, пока браузер не закончил полное построение страницы. Требуются нетривиальные ухищрения. Проходили в админке. Именно поэтому на первую загрузку страницы никакого аякса нет. (Ну и в скобках: заход на такую страницу вызовет не один, а два хита на сервер.)
Хотя правильнее сделать в getPage проверку на то, загружена ли страница.
Не спорю, вопрос решается в конечном итоге. Например, в нашем визуальном редакторе. Но назвать такое решение простым - язык не поворачивается. Спасибо дорогим разработчикам браузеров. Особый привет команде Microsoft.
PS Потому что обращаться к элементу ДО его объявления в коде - совсем уж моветон:
Я же не зря в примере JS опустил в самый низ.
Поэтому, необходимо также, чтобы в шаблоне сайта был вызов отложенных функций вывода переданных им значений:
<?$APPLICATION->ShowHeadScripts()?>
Их вызов необходимо разместить между тегами <head> и </head>, т.е. там же где и <?$APPLICATION->ShowCSS();?>
Шаблоны сайта, сделанные на основе последних демо-шаблонов, уже имеют вызов этих функций.
В противном случае, если этих функций вывода нет, то будете наблюдать ошибку, что CPHttpRequest не определен.
Бывают объемные скрипты, которые нужно запускать регулярно.
Их лучше бы запускать асинхронно, чтобы не тормозили загрузку страницы.
Я начал так делать - жизнь стала приятнее.
В прведенном эксперементе скрипт будет запущен один раз и 9 раз проверено время запуска.
Хотя, как оказалось, текущее время у меня записывалось после выполнения скипта. Так что, спасибо. Теперь все в порядке.
Для тех кто в танке - составляющая @X@ в ajax давно уже перестала быть обязательной - например достаточно часто для обмена данными используется не XML а JSON, либо (как в данном случае) передается один элемент и в XML-обвесе просто нет необходимости - лишние ресурсы на парсинг на стороне клиента. Впрочем эстетствующие персоны могут добавить - суть от этого не изменится
Константа "STOP_STATISTICS"
Константа "NO_KEEP_STATISTIC"
консоль ошибок
строка var TID = CPHttpRequest.InitThread();
CPHttpRequest is not defined
Добавлю в пост.
а накопипастить класс с незначительными изменениями придумали только в 6.5
зы. кстати, в скрипте множество мелких ошибок
Попробуйте изменить id дива в функции и у самого дива...
Всё просто перестаёт работать...
Вот код (backend):
<?
define('NO_KEEP_STATISTIC', true);
define('NOT_CHECK_PERMISSIONS', true);
define('LANG', 'ru');
require_once($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/main/include/prolog_admin_before.php"); // первый общий пролог
?>
<select name="select" id="select">
<?
$show = $_GET['id'];
if(CModule::IncludeModule("iblock"))
{
// если $ID не задан или это не число, тогда
// $ID будет =0, выбираем корневые разделы
$BID=7;
$ID=58;
// выберем папки из информационного блока $BID и раздела $ID
$items = GetIBlockSectionList($BID, $show);
while ($arItem = $items->GetNext()) {
echo "<option>".$arItem['NAME']."</option>";
}
}else{
ShowError("Модуль не установлен");
}
?>
</select>
<?
require($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/main/include/epilog_after.php");
?>
Вот код (frontend):
<?
require($_SERVER["DOCUMENT_ROOT"]."/bitrix/header.php");
$APPLICATION->SetTitle("Класс");
IncludeAJAX();
?>
<script type="text/javascript" src="/js/my.js"></script>
<script type="text/javascript">
function iblock_region(id, my) {
function ShowResult(data) {
myCloseWaitMessage('wait_container');
var obContainer = document.getElementById(my);
//alert (obContainer);
if (obContainer) {
obContainer.innerHTML = data;
//alert ('ogo');
}
}
myWaitMessage('обновление информации...','wait_container');
//PShowWaitMessage('wait_container');
var ADD = CPHttpRequest.InitThread();
CPHttpRequest.SetAction(ADD, ShowResult);
CPHttpRequest.Send(ADD, 'getdataklass.php', {'id':id});
return false;
}
</script>
<span id="wait_container" style="font-style:italic;"></span>
<div id="ajax_result_1" ></div>
<?
require($_SERVER["DOCUMENT_ROOT"]."/bitrix/footer.php");
?>