CGridView. Часть вторая. AJAX.

Владимир | | Ajax, PHP, Web разработка, Yii.

yii grid view

В этой части я хочу рассказать о некоторых особенностях реализации AJAX запросов в компоненте CGridView.

Предположим, у нас есть таблица, и мы создали для неё модель и скрипты для выполнения CRUD операций (с помощью встроенного генератора Yii).

Пусть таблица называется countries, содержит список стран с двумя полями (id, name).

В этом случае, страница управления записями будет доступна адресу

index.php?r=countries/admin

cgridview ajax

В нижней части страницы находится пагинатор (листалка). Клик по ссылке с номером страницы приведет к отправке 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.

По-умолчанию, если мы нажмем на кнопку просмотра (в последней колонке таблицы), то мы попадем на страницу соответствующей записи.

grid buttons

Мы изменим это поведение. Добавим под таблицей блок, в котором будут отображаться подробные сведения о выбранной записи, т.е. содержимое представления 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 предлагает продвижение сайта в интернете, поисковиках, а также его аудит.