jqGridPHP - таблицы на ajax без головной боли

fandm

Новичок
И ещё, насколько я правильно понимаю, функция renderColumn() выполняется только однажды, при первом рендеринге грида. Именно тогда и назначается
PHP:
$c['searchoptions']['defaultValue'] = $this->input($c['name']);
А что делать, если в первый раз грид был выведен со всеми записями, а потом его нужно отобразить с уже предустановленным фильтром?
Да, renderPostData() отработает и условие фильтра попадёт в $this->query, но defaultValue уже не заполнится и грид отобразится как бы с наложенным фильтром, но условие фильтра не будет отображено в FilterToolbar столбца.
Получается, надо делать полный ReRendering. Как?

Или же имитировать defaultValue на клиенте через
PHP:
			$('#gbox_'+gridid).find('#gs_'+column_name).each(function(){
				$(this).val(filter_value);
			});
 

fandm

Новичок
Другой вопрос. Определяю я свой обработчик opMyOp, делаю вызов:
PHP:
$grid.jqGrid().setGridParam({url : '?jqgrid=jqGrid&oper=MyOp'}).trigger("reloadGrid");
Обработчик выполняется, в нём я делаю:
PHP:
	protected function opMyOp()
	{
		throw new jqGrid_Exception('test');
	}
, в ответе приходит
PHP:
{ "error": "1", "error_msg": "test"...
, но errorHandler из jqgrid-ext.js не отрабатывает, т.е. не выводится ошибка в виде сообщения.
С чем это может быть связано? Неужели при таком подходе надо самому выводить сообщение об ошибке на loadComplete?

UPDATED.
Чувствую, что копать надо в сторону Inheritance, но пока, видимо, знаний маловато...

UPDATED.
Прямой вызов
PHP:
				loadComplete : function( data ){
					...
					/*мой код*/
					...
					$.jgrid.ext.errorHandler(data);
				},
выводит сообщение, но, видимо, у порождаемого диалога неверный z-index и сообщение появляется под моим диалогом, в котором расположен jqgrid. Хммм...

UPDATED.
Или вот эту фишку с oper вообще нельзя использовать в таком ключе?
 

Jnas

Новичок
Практика показывает, что готовые работающие примеры - доходчивее. :)
Верная практика, по крайне мере я большую часть кода понял по примерам. Но и документация конечно тоже помогает так скажем "допереть". Но всё же остались вопросы...
возвращаясь к вопросам, читал читал, но так и не понял, как сделать, куда копать и т.д.
чтоб гетом можно было передать 'статус' и обработать, пробовал как ниже, но ноль реакции (про экранирование не говорю:))
Даже упрощая:
PHP:
			if (!isset($_GET['status']) || $_GET['status'] == '') {
				$this->query = "select {fields} from YourTable
								where {where}";
			} else {
				$this->query = "select {fields} from YourTable
								where status=".$_GET['status']."
								AND {where}";
			}
 

fandm

Новичок
чтоб гетом можно было передать 'статус' и обработать
Открываем пример.
Переходим на вкладку PHP, чтобы посмотреть как это реализовано.
А там видим ключевые:
PHP:
    protected function renderPostData()
    {
        $data = array();
        
        foreach(array_intersect_key($this->input, $this->cols) as $k => $v)
        {
            $data[$k] = $v;
        }
        
        return $data;
    }
    
    protected function renderColumn($c)
    {
        if($this->input($c['name']))
        {
            $c['searchoptions']['defaultValue'] = $this->input($c['name']);
        }
        
        return $c;
    }
В renderColumn мы проверяем, если в массиве $this->input (по сути, он же $_REQUEST) передано среди прочего одно из имен столбцов грида, то мы это понимаем так, что параметрами в URL нам был передан "стартовый" фильтр. Поэтому для таких столбцов в searchoptions указываем опцию defaultValue, чтобы при визуализации грида на клиенте это defaultValue уже было прописано (применено) в FilterToolbar. Т.е. этим мы имитируем, будто пользователь ввёл условие фильтра в шапке соответствующего столбца (столбцов).
В renderPostData мы переписываем параметры, совпадающие с названиями столбцов в нашем гриде, переданные в $this->input (по сути, он же $_REQUEST), в массив $data, чтобы позднее в jqGridPHP произошла подстановка таких параметров вместо {where} в $this->query.

Таким образом, переопределение вот этих двух функций позволяет нам через URL передать фильтры по любому кол-ву столбцов и грид на клиенте визуализируется с учётом этих фильтров. В вышеуказанном примере в URL и передаётся
PHP:
&delivery_type=2&price=>1000
delivery_type и price - это столбцы в гриде. При этом результирующий SQL можно глянуть через Firebug. Он будет таким:
PHP:
SELECT i.id AS id, i.order_id AS order_id, o.delivery_type AS delivery_type, o.delivery_cost AS delivery_cost, CONCAT(c.first_name, ' ', c.last_name) AS customer_name, b.name AS name, i.price AS price 
FROM tbl_order_item i JOIN tbl_books b ON (i.book_id=b.id) JOIN tbl_order o ON (i.order_id=o.id) JOIN tbl_customer c ON (c.id=o.customer_id) 
WHERE o.delivery_type = '2' AND i.price > '1000' ORDER BY id asc LIMIT 20 OFFSET 0
Вот мы и видим, что подставились наши условия фильтров.

Но такой подход сработает, если jqgrid впервые рендерится (выводится) на странице. Если же сначала jqgrid выводится без фильтров, а в процессе работы потребуется наложить некий фильтр через reloadGrid, то фильтр применится, но в FilterToolbar не будут прописаны условия фильтров, их придётся "дописать" самому. Об этом я писал выше. Но может Вам это и не надо. Это обычно надо, когда грид расположен на диалоге, который визуализируется позже по нажатию некой кнопки. Для выбора значения из справочника, например.

У меня, к примеру, в проекте идёт использование jqgrid в диалогах для выбора значений из справочников. При этом требуется, чтобы грид сразу визуализировался с предустановленным фильтром. Поскольку у меня при загрузке страницы такие гриды, назовём их служебными, инициализируются сразу, то фильтра в них изначально нет. Поэтому предложенный выше вариант мне не подходит (по крайней мере если ~WR~ не даст ответа на мой вопрос выше :) ) и я пользуюсь установкой фильтра исключительно на клиенте. Я описывал этот вариант здесь. Т.е. происходит полная имитация, будто пользователь ввёл условие фильтра и нажал Enter.
 

fandm

Новичок
~WR~,
помогите, если можете, совершенно потерялся уже. Вопрос выше - это как бы одно, но появился новый вопрос. Или я туплю окончательно или... Вот я выполнил какой-то код в своей функции opMyOp. В конце в качестве ответа в грид приходит только {"success"=>1} и, соответственно, данные в грид уже не попадают. Что я делаю не так? Благодаря чему после выполнения кода в том же opEdit данные остаются в гриде?
 

~WR~

Новичок
Да делайте на клиенте и не заморачивайтесь.
Оно для того и создано, чтобы разгрузить вас от реализации тривиальных вещей и дать возможность сконцентрироваться на всяких необычных штуках.

Да и кода тут выйдет на пять строк.

По обработке ошибок от custom-операций. Да, её следует делать самостоятельно.
Без дополнительной оберточной функции на них даже общий обработчик нельзя повесить. Там же чистые вызовы $.get и $.post.

Можно использовать $.jgrid.ext.errorHandler. Но, когда идет большое наслоение диалогов, бывают проблемы с z-index. Их можно поправить через jQuery.css опять же, но самое простое и надежное - использовать alert(ret.error_msg).
 

~WR~

Новичок
Благодаря чему после выполнения кода в том же opEdit данные остаются в гриде?
Благодаря .trigger('reloadGrid'). После стандартных операций всё просто полностью перегружается.
Это намного проще, чем возвращать что-то на клиент и пытаться это вставить с учетом всех formatter'ов, привязкой event'ов и так далее.

Как-то так.
PHP:
$.getJSON($grid.getGridParam('url'), {
    'oper' : 'myOp',
    'id'   : $grid.getGridParam('selrow'),
    'foo'  : 'bar'
}, function(ret)
{
    if(ret.success)
    {
        $grid.trigger('reloadGrid');
    }
    else
    {
        alert(ret.error_msg);
    }
});
 

fandm

Новичок
Благодаря .trigger('reloadGrid')
"Шьёрт побьери", не поверите! :) Пока ехал домой, меня именно эта мысль и осенила, вспомнил код в базовом editGridRow (во внутренней функции postIt()):
PHP:
if(rp_ge[$t.p.id].reloadAfterSubmit) {
	$($t).trigger("reloadGrid");
	...
Спасибо! :)
Получается .trigger("reloadGrid") надо делать исключительно по положительному ответу от $.getJSON, т.к. если сделать это на loadComplete, то произойдёт зацикливание?

По обработке ошибок от custom-операций. Да, её следует делать самостоятельно.
Ок, спасибо!

но самое простое и надежное - использовать alert(ret.error_msg).
Понял. :) Я как раз вместо alert везде использую jQuery UI Dialog с одной кнопкой "Ок". Сделал на клиенте для этого простую функцию ShowMessage(title,msg_text). ;) А z-index вычисляю динамически непосредственно перед выполнением .dialog('open') с помощью такой функции:
PHP:
function max_zIndex(){
	var maxZ = Math.max.apply(null,$.map($('body > *'), function(e,n){
		   if($(e).css('position')=='absolute')
				return parseInt($(e).css('z-index'))||1 ;
		   })
	);
	return maxZ;
}
Благодаря этому ShowMessage, как говорится, always stay on top.
 

~WR~

Новичок
Получается .trigger("reloadGrid") надо делать исключительно по положительному ответу от $.getJSON, т.к. если сделать это на loadComplete, то произойдёт зацикливание?
Скорее всего, так.

У диалогов в jQuery UI есть штатный метод .dialog( "moveToTop" ). ;)
 

fandm

Новичок
У диалогов в jQuery UI есть штатный метод .dialog( "moveToTop" ). ;)
Вот ёлки-палки! :) Тормознул! Спасибо! ;)

А вообще это выглядит как их недоработка. Ведь, если я делаю вывод модального диалога, то он априори должен появиться поверх всех окон. Логично? :)
 

fandm

Новичок
Ещё вопросик. А можно как-то gzip-ить json для jqgrid? Или я ерунду спросил? :)
 

fandm

Новичок
Понял, спасибо! Я понимаю, что оффтоп в некотором роде, но...

Но лучше на уровне вебсервера.
Это об этом речь?

А если уже на уровне движка используется gzip-ование, то к чему приведёт его включение ещё и на уровне Apache-а? И воспримет ли тогда jqGrid корректно тот json, который к нему придёт?

UPDATED.
Всё, решил уже задачу. :) Вот хорошая инструкция для Apache 2. Не помешает и включение кеширования, о кот. упоминается в статье. Гораздо быстрее стало работать веб-приложение да и json "воспринимается" jqGrid быстрее.

UPDATED.
Единственное, с чем пока ещё не разобрался, так это как кешировать ajax-запросы. Вот двигаемся мы в гриде по страницам с записями, записи всё те же, всё на тех же страницах и не меняются, зачем их запрашивать с сервера, если можно подтянуть из кеша?
 

fandm

Новичок
Обнаружил странную вещь.
Если в опциях jqGrid прописать
PHP:
'treeGrid'      => true,
'datatype' => 'local'
, то получите ошибку в исходниках jqGrid.
А вот такой вариант
PHP:
'treeGrid'      => true,
'datatype' => 'jsonstring'
работает корректно и даёт тот же результат (в том смысле, что jsonstring = local).

Но если вы на клиенте для jqGrid с такими настройками попытаетесь сделать вот так
PHP:
$grid.jqGrid('setGridParam',{datatype:'json'}).trigger('reloadGrid');
, то будьте готовы, что ajax-запрос на сервер послан не будет, т.е. записей после .trigger('reloadGrid') вы не получите.
Просто надо обязательно сделать так
PHP:
$grid.jqGrid('setGridParam',{datatype:'json'});
$grid.jqGrid('setGridParam',{treedatatype:'json'}).trigger('reloadGrid');
и всё заработает как положено.

К чему бы это?..
 

sirba

Новичок
Здравствуйте. Подскажите пожалуйста, а можно в фильтр для определенного поля вставить datepicker ? Спасибо
 

sirba

Новичок
Сделал по примеру -только у меня не происходит
Он заполняет строку поиска (например: '01.01.2011 - 16.10.2011') и вызывает обновление грида.
Именно не происходит обновления грида, где я что упустил?
 

fandm

Новичок
Именно не происходит обновления грида
Вообще вопрос, конечно, к ~WR~, но, думаю, дело в этом
мы используем чуть модифицированный...
Догадываюсь, что модификация, в том числе, состояла в вызове
PHP:
$grid[0].triggerToolbar();
Документация. Прокрутите до фразы
When we create toolbar search with filterToolbar we create additional methods as follow
Этот метод заставляет примениться фильтр.
Хотя я бы оставлял это на откуп пользователя. Пусть сам нажимает Enter. Вдруг он хочет ещё какой-то фильтр ввести по другому столбцу?
 
Сверху