Внедрение Ajax в enFrame Внедрение аякса в фреймворк подразумевает написание ряда вспомогательных javascript функций и минимальное изменение php кода. В зависимости от задачи изменяются уже имеющиеся библиотеки либо создаются новые. Общая схема добавления ajax функциональности следующая: ● Как правило, меняющийся с помощью ajax участок заключается в <div></div> теги со специальным идентификатором. Желательно, чтобы этот идентификатор возвращала сама система (например, префикс дерева или пагера) ● Пишутся специальные селекторы, отслеживающие клики по нужным элементам ● Javascript получает информацию из объектов (например, получает значение параметра id из нажатой ссылки) и анализирует ее. Javascript функции отправляют информацию с помощью ajax запроса в enFrame. ● enFrame возвращает результат, который подставляется в нужный <div> Как написать класс, который сохраняет базовую функциональность и в то же время обеспечивает поддержку ajax? Нужно стремится к тому, чтобы наследуемый класс не изменял функциональность и логику поведения базового класса. Если посмотреть на класс аякс пагера (frw_ajax_pager.inc), то будет видно что класс нужен только для описания используемых шаблонов. Аякс класс, как правило, будет указывать только нужные шаблоны, иногда анализируя какие из них нужно использовать. Если нужно дописать отдельную функциональность, то это, как правило, не является задачей аякс классов. Иногда это делается с помощью javascript функций, получающих информацию и отдающих нужную часть. Для того чтобы создать собственный аякс класс, нужно унаследовать тот класс, функциональность которого будет переводится на аякс. В enFrame (класс FRW_Web_App) есть специальное свойство, которое показывает работает ли система в аякс режиме. Например, в простом режиме нужно чтобы использовался один шаблон, а в аякс режиме – другой. Пример из frw_ajax_pager.inc: if ($this->view->module->app->app_work_mode == FRW_WORK_MODE_AJAX) { $this->use_tmpl('pager_body2'); } else { $this->use_tmpl('pager_body'); } Данный участок кода проверяет, находится ли система в простом режиме (загружена в первый раз), либо осуществляется обработка аякс запроса. В данном случае это нужно для того, чтобы первый раз при обычной загрузке страницы отобразить <div></div> теги со всем содержимым (шаблон pager_body), а в следующие разы при аякс запросах отдавать только содержимое без <div></div> тегов (шаблон pager_body2). Так как обновление происходит с помощью obj.innerHTML, это позволяет избежать дублирования <div></div> тегов при каждом запросе. Как работать с селекторами и писать ajax запросы? В качестве базовых библиотек используется Prototype (http://prototype.conio.net/ , http://www.sergiopereira.com/articles/prototype.js.html) и Event-Selectors (http://encytemedia.com/event-selectors/). Найти библиотеки можно в javascript репозитории enFrame. Подключать их лучше в главном шаблоне приложения. Prototype позволяет быстро и удобно работать с ajax запросами, элементами страницы и т.д. Селекторы нужны для того, чтобы вынести отдельно javascript код, реагирующий на разные события). В рамках enFrame стоит избегать добавлять javascript прямо в html. Плохой пример внедрения javascript: <a href='index.html' onClick='return window.confirm("Are you sure?");'>Link</a> Селекторы позволяют отслеживать разные события и передавать управление пользовательским функциям. Пример: var Rules = { '#some_id a:click': function(element, event) { reg = /.+_of=(\d+).+/i; var arr = reg.exec(element.href); MakeAjaxResponse(arr[1]); Event.stop(event); } } Ajax.Responders.register({ onComplete: function() { EventSelectors.assign(Rules);} }) function init_rules() { EventSelectors.start(Rules); } Event.observe(window, 'load', init_rules, false); Данный код перехватывает события нажатия на ссылку внутри объекта с id равным 'some'. После этого анализируется href свойство ссылки, выбирается id и посылается запроc к enFrame c помощью функции MakeAjaxResponse (функция не является специальной и может называться/содержать все что необходимо). Как правило, с помощью селекторов получают нужный элемент, потом из него выбираются нужные данные (в данном случае, id), а потом отправляется аякс запрос. Сама отправка аякс запроса может быть осуществлена следующим образом: function MakeAjaxResponse(id) { var url = 'index.html?'; var pamars = 'mode_ajax=1&item_id'+id; var myAjax = new Ajax.Updater ( some_div, url, { method: 'post', parameters: pamars } ); } Данная схема является обычной для Prototype, почитать подробнее про отправку аякс запросов можно на сайтах, посвященных этой библиотеке. Видно, что функции MakeAjaxResponse в виде аргумента передается id. Это тот самый id, который был извлечен из ссылки на странице с помощью javascript. Иногда параметров может быть больше. В любом случае, необходимо сначала посмотреть как работает стандартный enFrame класс, какие параметры использует и откуда их берет. Потом необходимо с помощью селекторов перехватить нажатия по ссылкам (или другим элементам) и в зависимости от их содержания вызвать определенные javascript функции, которые в последствии отправят к enFrame нужный запрос. Всю Get строку (которая потом присваиваются свойству parameters см. выше) нужно формировать самостоятельно, выбрав необходимые значения из объекта с помощью javascript (например, распарсив свойство href у ссылки). Система в ajax режиме Нужно обратить внимание на передаваемый параметр mode_ajax=1 в Get строке. Когда enFrame получает переменную mode_ajax равную единице, система начинает работать в аякс режиме. Это означает, что все модули, у которых нет функции is_ajax_enabled() равной true, отрабатываться не будут. Чтобы модуль работал в аякс режиме нужно добавить к класс модуля метод function is_ajax_enabled() { return true; } В зависимости от полученных параметров, при каждом обращении enFrame собирает ответы из всех модулей в виде большой строки. Если включен аякс режим, то строку отдадут только модули с методом is_ajax_enabled() равным true. Получается, что при аякс запросе мы получим только тот кусок кода, который нам нужно вставить на место старого. Этот кусок кода подставляется в нужный div,а сама страница остается неизмененной и не перегружается. Куда складывать написанный javascript и как правильно его подключать? Лучше всего отдельные функции, в которые не нужно подставлять {*tmpl*} переменные, вынести в отдельный .js файл, который будет располагаться в папке scripts (пример таких функций – scripts/ajax_pager.js). Функции инициализации и описание селекторов, как правило, лучше вынести в отдельный шаблон с именем inline_js. Если посмотреть файл tmpl/frw_ajax_pager.tmpl, то видно что ряд js кода вынесено в специальный шаблон. В этом шаблоне присутствует переменная {*pfx*}, которая подставляется системой автоматически. Эта переменная может называться по-другому, ее можно определить самостоятельно, создать несколько переменных и т.д. Главное, чтобы этот код находился внутри шаблона и парсился системой. Чтобы подключить этот код в приложение нужно вызвать следующий метод (как правило, в методе run): $this->view->module->app->attach_js($this->tmpl->render_tmpl('inline_js', &$this)); Метод attach_js добавляет в специальную переменную enFrame javascript строку, сгенерированную аякс классом. Код $this->tmpl->render_tmpl('inline_js', &$this) как раз возвращает эту строку, рендеря шаблон inline_js. Часто в index.tmpl приложения вставляется строка <script type="text/javascript"> {*inline_js*} </script> которая в случае необходимости будет содержать селекторы и функции, так как все методы attach_js будут собирать из разных аяксовых классов нужные данные и парсить их в {*inline_js*}. В исключительных случаях для получения нужного html кода в качестве ответа на аякс запрос возникает необходимость экстренно завершить работу enFrame (вплоть до exit;). В таких случаях необходимо внимательно отследить поведение системы, сохранить нужные параметры и т.д.