Добрый день. Сегодня мы рассмотрим довольна сложный пример - я постараюсь Вам рассказать как правильно использовать аякс в ваших компонентах. MVC: Не вижу смысла перепечатывать стопяцот букв про то, что такое МВЦ и с чем его едят, вот несколько источников: - вебфлешер.ру - Записки начинающего веб-мастера Рекомендую у ОБЯЗАТЕЛЬНОМУ прочтению этих статей по MVC, иначе, без понимания MVC, весь оставшийся материал будет для вас безполезен. 1. Структура компонента В нашем примере мы рассмотрим компонент с одним контроллером и несколькими моделями. Каждая модель будет иметь свое представление и будет отвечать за свой "уровень вложенности", т.е. мы реализуем многоуровневый компонент. Я думаю этот пример идеально подойдет для реализации телефонного справочника. Ниже приведена схема передачи данных внутри нашей МВЦ-модели 1.1 Контроллер (controller.php) Как мы видим из схемы, после того как пользователь попадает на страницу компонента управление передается в контроллер, который в свою очередь, в зависимости от полученных параметров решает какой модели передать управление и, соответственно, какие данные вернутся к нам обратно. Код (PHP): function display() { $this->_checkParams(); parent::display(); } Основной метод контроллера - display(), в листинге мы видим, что вызывается еще один метод checkParams(), который и определяет - какие параметры были переданы и что нам дальше делать ) Код (PHP): private function _checkParams() { $cur_param = JRequest::getInt('c_option'); switch ($cur_param) { case 1: sleep(3); JRequest::setVar('view', 'model1' ); $this->getView('model1','html'); break; case 2: sleep(3); JRequest::setVar('view', 'model2' ); $this->getView('model2','html'); break; case 3: sleep(3); JRequest::setVar('view', 'model3' ); $this->getView('model3','html'); break; default: JRequest::setVar('view', 'textcomponent' ); $this->getView('textcomponent','html'); } } Из листинга видно, что мы получаем из адресной параметр "c_option" и в зависимости от его значения передаем управление в соответствующую модель. Если "c_option" пустой или не попадает не в одно из условий - показываем стартовую страницу компонента. 1.2. Стартовая страница Для отображения главной страницы компонента мы не используем ни какой модели. Из контроллера управление сразу же переходит в представление Код (CODE): views/textcomponent/view.html.php: Код (PHP): defined( '_JEXEC' ) or die( 'Restricted access' ); jimport( 'joomla.application.component.view'); class textcomponentViewtextcomponent extends JView { function display($tpl = null) { $greeting = "Стартовый шаблон!"; $iID = JRequest::getInt('Itemid'); $cName = JRequest::getString('option'); $this->assignRef( 'greeting', $greeting ); $this->assignRef( 'itemID', $iID ); $this->assignRef( 'cName', $cName ); parent::display($tpl); } } Тут мы получаем параметры из адресной строки - Itemid и option. (Это сделано для того, что бы после переименования компонента не нужно было править код.) И выводим соответствующий шаблон Код (CODE): view/textcomponent/tmpl/default.php Код (PHP): <?php defined('_JEXEC') or die('Restricted access'); $com_path = 'components/com_textcomponent/js/'; JHTML::script('jquery.js' , $com_path ); JHTML::script('ajax_history.js' , $com_path ); JHTML::script('main.js' , $com_path ); ?> <link rel="stylesheet" href="/components/com_textcomponent/css/main.css" type="text/css" /> <h1><?php echo $this->greeting; ?></h1> <div id="main-component-div"> <a href="index.php?option=<? echo $this->cName ?>&Itemid=<? echo $this->itemID ?> &c_option=1" name="firstlvl" id="start-link"> Переход на первый уровень </a> <div id="ajax-data"></div> <div id="ajax-loader"></div> </div> Тут мы подключаем 3 JS библиотеки: Код (CODE): jquery.js - ядро фреймворка JQuery ajax_history.js - плагин для сохранения истории в браузере при использовании аякса (работает не во всех браузерах) main.js - библиотека в которой будут описаны все функции для работы с аяксом. Также мы имеем 3 слоя: Код (CODE): - main-component-div - основной слой, содержит в себе ссылку для перехода на следующий уровень компонента, а также еще два слоя для реализации аякса. - ajax-data - слой, куда мы будет загружать полученные данные - ajax-loader - слой, в котором будет отображаться индикатор работы приложения Визуальная схема прилагается 1.3. Передача управления модели На главной странице компонента у нас имеется сслыка и она содержит параметр "c_option=1". Это значит, что при нажатии на ссылку управление будет передано с начала контроллеру, т.к. значение = 1 будет выполнен следующий кусок кода: Код (PHP): case 1: sleep(3); JRequest::setVar('view', 'model1' ); $this->getView('model1','html'); break; и управление будет передано первой модели: Код (CODE): models/model1.php Код (PHP): defined( '_JEXEC' ) or die( 'Restricted access' ); jimport('joomla.application.component.model'); class TextcomponentModelModel1 extends JModel { public $_content; public $pathway; public $iID; public $cName; function __construct() { parent::__construct(); $this->_getParams(); $this->getData(); } private function _getParams() { $this->iID = JRequest::getInt('Itemid'); $this->cName = JRequest::getString('option'); } function addBCItem() { $pathway = TextcomponentController::getBCObject(); $pathway->addItem('Model1'); $this->pathway = TextcomponentController::getBCData(); } function getData() { $this->addBCItem(); $this->_content = "Model1"; return $this->_content; } } В классе TextcomponentModelModel1 мы видим 4 метода: Код (CODE): __construct() - конструктор класса _getParams() - метод для получения параметров option и Itemid addBCItem() - метод для работы со строкой навигации getData() - метод в котором мы получаем "нужные" нам данные ))) 1.3.2 Строка навигации Не так давно я написал статью "Модифицируем строку навигации (не безызвестный BreadCrumb)", где описал основные принципы работы со строкой навигации. Сейчас мы применим это дело на практике: Код (PHP): $pathway = TextcomponentController::getBCObject(); // получаем объект СН для модификации $pathway->addItem('Model1'); //добавляем новый элемент $this->pathway = TextcomponentController::getBCData(); //получаем хтмл-код модуля "mod_breadcrumbs" Функции getBCObject() и getBCData() находятся в контроллере и выглядят так: Код (PHP): function getBCObject() { $app =& JFactory::getApplication(); $pathway =& $app->getPathway(); return $pathway; } function getBCData() { jimport( 'joomla.application.module.helper' ); $gBC = JModuleHelper::getModule( 'mod_breadcrumbs'); $cBC = JModuleHelper::renderModule( $gBC ); return $cBC; } думаю комментарии излишни ) 1.3.3. Представление После того, как модель заканчивает свою работу вызывается представление Код (CODE): views/model1/view.html.php Код (PHP): defined( '_JEXEC' ) or die( 'Restricted access' ); jimport( 'joomla.application.component.view'); class TextcomponentViewModel1 extends JView { function display($tpl = null) { //отображаем вид parent::display(); } } с помощью которого мы отображаем шаблон Код (CODE): views/model1/tmpl/default.php Код (PHP): <?php defined('_JEXEC') or die('Restricted access'); $myModel = $this->_models["model1"]; ?> <div id="ajax-first-lvl" class="child"> <?php echo $myModel->pathway; ?> <a href="index.php?option=com_textcomponent&Itemid=120&c_option=2" name="firstlvl"> <?php echo $myModel->_content; ?> </a> </div> В шаблоне мы имеем ссылку для перехода к следующему уровню компонента и строку навигации (о ней чуть позднее). 2. Ajax И так, аякс. Работа с аяксом будет реализована с помощью фреймворка JQuery. В шаблоне по-умолчанию Код (CODE): views/textcomponent/tmpl/default.php мы подключали библиотеку main.js, листинг ниже Код (CODE): $(document).ready(function() { $("#ajax-loader").bind("ajaxSend", function() { $("#ajax-data").hide(); $(this).show(); // показываем элемент }).bind("ajaxComplete", function() { $(this).hide(); // скрываем элемент $("#ajax-data").fadeIn(1000); }); ajaxEvent(); }); function ajaxEvent() { var mDiv = "main-component-div"; var BCDiv = "pathway"; $("#" + BCDiv + " a[name='bc-ajax-mode']").click(function() { linkAction($(this)); return false; }); $("#" + mDiv + " a[name='firstlvl']").click(function() { linkAction($(this)); return false; }); } function linkAction(item) { var ajaxMode = "&tmpl=component&format=raw"; var cURL = item.attr("href") + ajaxMode; $("#start-link").hide(); $.post(cURL, getData); } function replaceBC() { var pItems = $("#ajax-data a.pathway"); var iLength = pItems.length; if(iLength > 0) { for(i = 2; i < iLength; i++) $(pItems[i]).attr("name", "bc-ajax-mode"); var PathwayContainer = $("#pathway"); var newPathway = $("#ajax-data span.breadcrumbs"); PathwayContainer.html(newPathway); console.log(pItems.length); } } function getData(msg) { $("#ajax-data").html(msg); replaceBC(); ajaxEvent(); } Рассмотрим подробнее: - блок $(document).ready(function() {...}); Код (CODE): $(document).ready(function() { $("#ajax-loader").bind("ajaxSend", function() { $("#ajax-data").hide(); $(this).show(); // показываем элемент }).bind("ajaxComplete", function() { $(this).hide(); // скрываем элемент $("#ajax-data").fadeIn(1000); }); ajaxEvent(); }); Как и в Joomla, в JQuery при использовании аякса возникают события, которые мы можем перехватить и навесить на них, нужные нам функции. Метод ready() объекта $(document) вызывается при полной закгрузке хтмл-страницы, т.е. DOM-дерево полностью готово и все элементы доступны. В этом методе мы навешиваем обработчики событий Код (CODE): ajaxStart - начало выполнения аякс-запроса ajaxComplite - окончание выполнения запроса (не обязательно удачное) Т.е. при старте запроса мы прячим слой с данными "#ajax-data" и показываем слой с индикатором работы аякса "#ajax-loader". Также мы вызываем функцию ajaxEvent() которая навешивает обработчики событий для известных DOM-элементов. Код (CODE): function ajaxEvent() { //id родительного слоя (см. схему) var mDiv = "main-component-div"; //id слоя, содержащего строку навигации var BCDiv = "pathway"; //перехватываем нажатие на ссылку с атрибутом //name='bc-ajax-mode' (строка навигации) $("#" + BCDiv + " a[name='bc-ajax-mode']").click(function() { //передаем в функцию объект ссылки на которую нажали linkAction($(this)); //выходим из обработчика, тем самым не даем браузеру перейти по действительному адресу return false; }); //перехватываем нажатие на ссылку с атрибутом //name='firstlvl' (строка навигации) $("#" + mDiv + " a[name='firstlvl']").click(function() { linkAction($(this)); return false; }); } Суть данной функции в том , что бы перехватить нажатия на ссылки с заданным именем и выполнить нужные нам действия, а точнее функцию linkAction(), которая в качестве параметра принимает объект элемента на которого было совершено нажатие: Код (CODE): function linkAction(item) { //необходимые POST-параметры для получения //только данных из компонента var ajaxMode = "&tmpl=component&format=raw"; //получаем адрес, который указан в атрибуте Href var cURL = item.attr("href") + ajaxMode; //прячем ссылку, которая находится //на стртовой странице $("#start-link").hide(); //делаем аякс запрос // и вызываем колбэк функцию $.post(cURL, getData); } Тут мы получаем урл из переданного, в качестве аргумента, элемента, добавляем параметры, с помощью которых мы сообщаем ядру джумлы, что хотим получить только данные, которые вернет нам наш компонент и не более того ) Выполняем аякс-запрос с помощью функции $.pos() , которая в качестве параметров принимает адрес скрипта, который должен обработать запрос , и имя колбэк функции, с помощью который мы обработаем результаты запроса. Код (CODE): function getData(msg) { //@msg - данные которые пришли в результате ответа сервера //запихиваем данные в <div id="ajax-data"> $("#ajax-data").html(msg); //подсовываем новую строкунавигации replaceBC(); //вешаем на созданные объекты обработчики ajaxEvent(); } Данные, которые вернет нам сервер, хранятся в переменной "msg". Помещаем их в нужный нам контейнер и навешиваем на вновь созданные DOM-элементы обработчики событий. На этом моменте я бы хотел заострить Ваше внимание - дело в том, что когда мы в $(document).ready(function() {...}); первый раз вызывали функцию ajaxEvent(), которая навешивала события на элементы, находящиеся на странице. Но после аякс-запроса мы получили новые элементы, на которые действие функции ajaxEvent() не расространяется, т.о. нужно вызвать эту функцию еще раз и обновить, нужные нам, обработчики. В getData() присутствует еще одна очень интересная функция replaceBC(), суть которой заключается в изменении строки навигации. Рассмотрим ее подробнее: Код (CODE): function replaceBC() { //получаем массив объектов <a> с классом pathway var pItems = $("#ajax-data a.pathway"); var iLength = pItems.length; if(iLength > 0) { //дописываем в них атрибут name = bc-ajax-mode for(i = 2; i < iLength; i++) $(pItems[i]).attr("name", "bc-ajax-mode"); //получаем контейнер, в котором находится настоящая СН var PathwayContainer = $("#pathway"); //получаем контейнер, в котором находится новая СН var newPathway = $("#ajax-data span.breadcrumbs"); //подставляем новую СН вместо старой (: PathwayContainer.html(newPathway); } } В обще, идея с модификацией и подменой строки навигации пришла ко мне не сразу, а в результате долго хождения по квартире, даже ушиб себе ногу ((( Но я очень рад что это ко мне пришло Итак, что было понятнее приведи структуру СН: Код (html): <div id="pathway"> <span class="breadcrumbs pathway"> <a class="pathway" href="http://avangard-sk.ru/"> Главная </a> <img alt="" src="/templates/rhuk_milkyway/images/arrow.png"> <a class="pathway" href="http://avangard-sk.ru/"> Наш компонент </a> <img alt="" src="/templates/rhuk_milkyway/images/arrow.png"> Модель1 </span> </div> Теперь, после выполнения аякс-запроса мы имеем две строки навигации: - Первая - настоящая, ее все видят но изменений в ней ни кто не наблюдает - Вторая - вновь полученная, с изменениями, но ее никто не видит, так же как и полученный контент, но это временно Итак, что мы делаем с помощью replaceBC() - получаем массив элементов СН, нас интересуют 3,4 и т.д. элементы, т.е. строка может выглядеть следующим образом: Код (CODE): [B][COLOR="Blue"][U]Главная [/U][/COLOR][/B]- [B][COLOR="Blue"][U]Наш компонент[/U][/COLOR][/B] - [B][COLOR="Blue"][U]Раздел1 [/U][/COLOR][/B]- [B][COLOR="Blue"][U]Подраздел1 [/U][/COLOR][/B]- [B][COLOR="Blue"][U]Подподраздел1 [/U][/COLOR][/B]- Предмет1 И таким образом первые два элемента СН - "Главная" и "Наш компонент" нас не интересуют, нам не нужно навешивать на атрибуты name="bc-ajax-mode", с помощью которых мы сможем перехватить переход по этим ссылкам. После того, как все элементы были изменены - берем "новую" СН и меняем ее на "старую". Код (CODE): PathwayContainer.html(newPathway); Принцип аякс-запроса в мвц модели: Заключение Вот в принципе и все. Считаю что для российского джумла-комъюнти статья будет очень полезна, т.к. в рунете нет нихрена подобного )))) Приветствую любую адекватную критику, также буду рад ответить на все ваши вопросы (по сабжу )) ) Пример работы. Сам компонент в аттаче.
Хорошая статья, но вот у меня вообщем такой вопрос, про плагин и jquery Если мы используем jquery в плагине и хотим чтобы данные передаваемые из скрипта леттели не в отдельный файл от плагина, а в тот же плагин в определённый обработчик, как правильно задвать УРЛ при посыле Json?