В этой части я хочу рассказать о некоторых особенностях реализации AJAX запросов в компоненте CGridView.
Предположим, у нас есть таблица, и мы создали для неё модель и скрипты для выполнения CRUD операций (с помощью встроенного генератора Yii).
Пусть таблица называется countries
, содержит список стран с двумя полями (id
, name
).
В этом случае, страница управления записями будет доступна адресу
index.php?r=countries/admin
В нижней части страницы находится пагинатор (листалка). Клик по ссылке с номером страницы приведет к отправке ajax запроса. Но вот тут появляется одна из особенностей. Этот ajax запрос возвращает не только новую страницу с данными, но и всю страницу целиком. Начиная от doctype
и заканчивая закрывающим тегом html
.
Если на странице ничего, кроме таблицы нет, то это не проблема. Но если мы разместим на ней несколько виджетов, для формирования которых нам потребуется выполнить дополнительные запросы к БД, то смысла в такой ajax пагинации окажется немного.
В общем, нужно, чтобы при клике на номер страницы сервер отправлял только фрагмент разметки с таблицей.
Сделать это несложно. Для начала вынесем из представления views/countries/admin.php
код, который выполняет формирование таблицы в отдельный файл — views/countries/admingrid.php
.
Речь идет об этом фрагменте
<?php $this->widget('zii.widgets.grid.CGridView', array( 'id'=>'countries-grid', 'dataProvider'=>$model->search(), 'filter'=>$model, 'columns'=>array( 'c_id', 'c_name', array( 'class'=>'CButtonColumn', ), ), )); ?>
На его место в views/countries/admin.php
добавляем следующий код.
<?php $this->renderPartial('admingrid', array('model'=>$model)); ?>
Идея в том, что если мы получим ajax запрос, то возвращать будем только содержимое admingrid.php
, если запрос будет обычный – всю страницу.
Теперь переделываем метод actionAdmin
контроллера.
public function actionAdmin() { $model=new Countries('search'); $model->unsetAttributes(); // clear any default values if(isset($_GET['Countries'])) $model->attributes=$_GET['Countries']; if (isset($_GET['ajax'])) { $this->renderPartial('admingrid',array( 'model'=>$model, )); } else { $this->render('admin',array( 'model'=>$model, )); } }
Здесь добавлена дополнительная проверка (строка 8). Класс CGridView
имеет свойство ajaxVar
, которое по-умолчанию равно ajax. Это свойство задает имя GET параметра, который указывает, что данный запрос является ajax запросом.
Если этот параметр установлен, мы используем метод renderPartial
, который возвращает браузеру только содержимое представления admingrid
. В противном случае, вызываем метод render, который формирует всю страницу целиком.
Внешне в работе страницы ничего не изменилось, но если вы посмотрите с помощью firebug’а ответы сервера на ajax запросы, то разница будет заметной.
Теперь сделаем просмотр записей с помощью AJAX.
По-умолчанию, если мы нажмем на кнопку просмотра (в последней колонке таблицы), то мы попадем на страницу соответствующей записи.
Мы изменим это поведение. Добавим под таблицей блок, в котором будут отображаться подробные сведения о выбранной записи, т.е. содержимое представления views/countries/admingrid.php
.
Чтобы не менять стандартное поведение кнопок в таблице мы добавим к ней ещё один столбец со ссылками «Предпросмотр
».
При клике по этой ссылке под таблицей появится подробная информация о данной записи.
Приступим.
Добавляем новую колонку в таблицу. Для этого изменим файл admingrid.php
следующим образом.
<?php $this->widget('zii.widgets.grid.CGridView', array( 'id'=>'countries-grid', 'dataProvider'=>$model->search(), 'filter'=>$model, 'afterAjaxUpdate'=>'function(id, data) {$.fn.setPreviewLinksHandler(id, data);}', 'columns'=>array( 'c_id', 'c_name', array( 'class'=>'CButtonColumn', ), array( 'type'=>'raw', 'value'=>'CHtml::link("Предпросмотр", array("countries/view", "id"=>$data->c_id, "ajax"=>"preview"), array("name"=>"previewLink"))', ), ), )); ?>
Здесь есть нюансы. Во-первых, при добавлении столбца необходимо указать, что он имеет тип raw
, иначе по-умолчанию Yii применит фильтры к его содержимому.
Во-вторых, нам нужно назначить каждой ссылке обработчик события onClick
. Причем, сделать это необходимо как при первоначальной загрузке страницы, так и при «перелистовании».
Для этого мы используем свойство afterAjaxUpdate
(строка 6). Ему присваиваем JavaScript функцию, которая будет вызвана после обновления таблицы с помощью ajax метода.
Теперь напишем функцию setPreviewLinksHandler
. Для этого создаём файл js/previewlinks.js
.
$(function() { $.fn.setPreviewLinksHandler = function(id, data) { $("a[name=previewLink]").click(function() { var link = $(this); $.get(link.attr('href'), function(data) { $('#preview').html(data); }); return false; }); }; $.fn.setPreviewLinksHandler(); });
Эта функция получает в качестве параметров id
блока с таблицей и её содержимое (data
). Но, т.к. содержимое таблицы мы менять не будем, то и эти параметры нам не нужны.
Наша функция setPreviewLinksHandler
ищет все ссылки, у которых атрибут name равен previewLink
и назначает им обработчик события onClick
.
Этот обработчик формирует и отправляет ajax запрос, который возвращает подробную информацию о записи.
Тут обратите внимание, что при отправке ajax запроса используется атрибут href
ссылки, таким образом, при отключенном JS клик по ссылке будет отправлять пользователя на страницу с подробной информацией о записи.
Рассмотрим метод actionView
контроллера.
public function actionView() { if (isset($_GET['ajax'])) { $this->renderPartial('viewrow',array( 'model'=>$this->loadModel(), )); } else { $this->render('view',array( 'model'=>$this->loadModel(), )); } }
Здесь мы проверяем, есть ли в параметрах запроса атрибут ajax
, и если он есть, показываем представление viewrow
. При этом используется метод renderPartial
. В противном случае, просто показываем представление view
.
viewrow.php
создаём по тому же принципу, что и admingrid.php
. Перемещаем код создания виджета CDetailView
в viewrow.php
.
<?php $this->widget('zii.widgets.CDetailView', array( 'data'=>$model, 'attributes'=>array( 'c_id', 'c_name', ), )); ?>
А в view.php
добавим
<?php $this->renderPartial('viewrow', array('model'=>$model)); ?>
В результате этих манипуляций подробные сведения о записи будут появляться под таблицей при клике по ссылке «Предпросмотр».
Я надеюсь, что никого не запутал своими объяснениями 😉 Чтобы вам удобнее было экспериментировать, выкладываю архив с исходниками этого примера и дампом базы (предварительно вам нужно будет создать приложение Yii).
SourceЕсли есть вопросы или замечания, пишите! Особенно интересуют альтернативные варианты решения таких задач 🙂
Интересно почитать
Компания Intelsib предлагает продвижение сайта в интернете, поисковиках, а также его аудит.