Filtrovací formulář + AJAX + stránkování

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
joe
Člen | 313
+
0
-

Ahoj,

potřeboval bych poradit, jak udělat formulář, kterým budu filtrovat pomocí AJAXu a následně filtrované položky budu moci stránkovat.

Nepoužívám komponentu, vše mám v presenteru, kde je továrnička na formulář (metoda GET), na Paginator. Filtrování mi v současné době funguje, ale jakmile kliknu na další stránku na paginatoru, tak se mi nepoužijí ty hodnoty z filtrování, takže sice se položky stránkují, ale ne vyfiltrované.

Zajímalo by mě tedy, kam a jak mám ukládat ty hodnoty z filtrování? Zkoušel jsem se dívat do DataGridu, ale nějak to z toho nemůžu vyčíst, ještě se v tom tolik neorientuji.
Zkoušel jsem dát perzistentní parametry do presenteru, ale odkazy stránkovače jsou pořád ve tvaru:

…?paginator-page=2

Díky moc za rady

jasir
Člen | 746
+
0
-

Perzistentní parametry by měly fungovat, ukaž kód.

joe
Člen | 313
+
0
-

Tak posílám kód, dost jsem to zjednodušil, ale jinak to mám stejně:

<?php

class MyPresenter extends BasePresenter {

/** @persistent string */
public $filter = '';

public function renderDetail() {
  $form = $this['filterForm'];

  // rozpoznani, jestli byl form odeslan a podle toho vyber polozek
  $items = Items::get();
  // pokud by byl formular odeslan, tak bych do atributu $filter ukladal hodnoty z formulare ve tvaru URL: ...&param1=value&param2=value
  // a protoze je perzistentni, ocekaval bych to pak i u tech odkazu v paginatoru
  if($this->isAjax()) {
    $this->invalidateControl('items');
    $this->invalidateControl('paginator');
  }

}

}
?>
A v šabloně mám jen jednoduchý výpis:

{control formFilter}

{snippet items}
{foreach $items as $item}
  ...
{/foreach}
{/snippet}

{snippet paginator}
  {control paginator}
{/snippet}

Editoval joe (20. 2. 2011 3:54)

joe
Člen | 313
+
0
-

Tak samozřejmě má chyba, přehlédl jsem, že nenastavuji ten perzistentní parametr u všech vlastností a pak jsem to filtroval jinými.
Teď se tam už vkládá.

Ale co mi zase vadí je to, že u všech odkazů vedoucí do samého presenteru budou ty perzistentní parametry. Odkazy na detaily bych chtěl mít jen …/detail/4

No zkrátka s tím, co se mi teď ukládá do $filters bych chtěl mít odkazy jen v Paginatoru, nikde jinde.

Nebo by to šlo celé udělat nějak jinak – lépe? Prosím o nakopnutí…

bojovyletoun
Člen | 667
+
0
-

Informace o frázi a o stránce se přece musí někde uchovávat? Jedna z možnostní jr právě URL.
Duhá možnost je v session – mě napadlo k tomu účelu použít metody loadstate a savestate u komponenty – podrobněji zde jinak jsem to v praxi nerealizoval.
EDIT: realizoval jsem první část s kódem. Ta druhá od „jak to vylepšit“ je ten nápad, co jsem zde popisoval.

Jo ještě nezapomeň po změně hledaného výrazu nastavit první stranu.

Editoval bojovyletoun (20. 2. 2011 12:50)

joe
Člen | 313
+
0
-

Právě bych tu informaci chtěl přenášet u odkazů, ale jen u těch, co jsou v paginatoru. Nechtěl bych do toho tahat sessions, už z toho důvodu, že používám schválně GET pro neAJAXové požadavky, aby bylo možné zkopírovat adresu. S AJAXem bude použitý #.

Pro představu:

Mám presenter ItemPresenter, v renderDefault() vypisuju všechny položky, v renderDetail($id) detail jen jedné.

Takže u odkazu na detail v šabloně {plink detail 5}, bych chtěl, aby pořád zůstal
../detail/5

a nepřidával parametry s filtrováním, protože je tam stejně nevyužiju, akorát se tím kazí jednoduchá adresa. V případě stránkování ale chci, aby tam byly (jinak by to ani nešlo):

../?do=formFilter-submit&param1=value&param2=value&…

V případě těch persistentních se tam ale přidají, což je správné, ale nechtěné. Je možné nějak ty parametry resetovat nebo nějak nastavit, aby se u některých odkazů negenerovaly?

loadState & saveState
K ukládání stavu jsem se včera taky dostal, ale moc jsem nepochopil jak na to. Mj. jsem na fóru četl, že to z presenteru bylo odstraněno (ale byl to starý příspěvek), nicméně teď to u něj mám. Zkoušel jsem metodu saveState(), která nevím proč, ale zavolá se aspoň 6×.

Editoval joe (20. 2. 2011 13:35)

joe
Člen | 313
+
0
-

Tak dalo by se to vyřešit tak, že si do Paginatoru přenesu ty parametry, které tam chci mít. Zároveň bych ale chtěl, aby se po kliknutí na odkaz v něm odeslal i formulář, parametry se ale dávají do URL s prefixem paginatoru:

?paginator-page=5&paginator-do=formFilter-submit

a já bych potřeboval

?paginator-page=5&do=formFilter-submit

To by nějak vyřešit šlo?

Ani
Člen | 226
+
0
-

Jestli máš te řetězec hodně dlouhej, tak si z něj můžeš parametry pro filtr někam ukládat, dělat z toho hash a ten dávat do url. Pak jen načteš filtr podle řetězce (hashe). Docílí se tím toho, že ta url nebude mít třeba několik set znaků.
Jinak co píšeš pak dál mi není moc jasný, to jednou chceš zpracovat formulář jako POST a jidny zas předávat jako GET.

joe
Člen | 313
+
0
-

Udělat z hodnot parametrů hash? A jak se ty hodnoty pak dozvim? :-) Uvítám nějaké zkrácení odkazu, asi jsem nepochopil jak jsi to myslel.

Vysvětlím to trochu podrobněji. Snad to pak bude úplně jasné.

Mám ItemPresenter s renderDefault (výpis všech položek) a renderDetail($id) (výpis konkrétní). Na výpisu všech položek mám filtrovací formulář, kde je možné změnit cca. dvacet hodnot.

Filtrovací formulář se vždy bude posílat metodou GET. Bez AJAXu dostanu správnou URL a s AJAXem budu měnit část za #.

Odkazy na detaily jednotlivých položek chci mít:
/item/detail/3
ne takto:
/item/detail/3?do=formFilter-submit&param1=value&param2=value&param3=value&...

S tím, že odkazy v Paginatoru musí být právě:
/item/?paginator-page=2&do=formFilter-submit&param1=value&param2=value&param3=value&...

protože při stránkování potřebuji vědět, podle čeho se filtruje. Tzn., že musím při kliknutí na další stránku docílit odeslání formuláře. Při {action}Default totiž kontroluju jeho odeslání a následně na to vybírám položky z modelu (db).


Potřeboval bych nějaké jednoduché a funkční řešení. Proto mi přijde zbytečné ukládat si parametry jinam, když je pokaždé dostávám v URL adrese.

Zatím to asi vyřeším tak, že v Paginatoru si dám nějaký atribut public $filter, který budu předávat do jeho šablony a tam odkazy vypisovat

<a href="{link this, 'page' => $paginator->page + 1}{$filters}">Další stránka</a>

a v presenteru ItemPresenter, konkrétně v renderDefault budu paginatoru nastavovat tu hodnotu $filters.

Možná to nebude nejlepší řešení, ale jiné mě nenapadá. Jakékoli další rád uvítám.

Filip Procházka
Moderator | 4668
+
0
-
$q = &$presenter->request->params['q'];
# nebo z persistentniho parametru

$session = $this->getSession('FilterQuery');

// save
$q = substr(md5(serialize($arrayOfṔarams)), 0, 6);
$session[$q] = $arrayOfParams;
# nezapomenout předávat v query

// read
$arrayOfParams = $session[$q];

Editoval HosipLan (20. 2. 2011 15:47)

Ani
Člen | 226
+
0
-

Tak píšu, že se ten hash musí někam ukládat.

Myslím že by mělo fungovat, když v renderDefault() jako paremtry vypíšeš proměné filtru a v detail() jen id.

Editoval Ani (20. 2. 2011 16:03)

joe
Člen | 313
+
0
-

Jó díky, vůbec mi to nedocvaklo s tím hashem, jak to bylo myšlené. Asi toho ještě využiju, ještě zvážím, možná to je taky zbytečné, protože pak se zase nemůžou jednoduše hodnoty v URL přepisovat.

Nakonec jsem to teda vyřešil nějak takhle. Asi to má nějaké mouchy, ale u mě se to zdá být funkční. Do paginatoru si předám to $filters, které generuju z požadavku.

<?php
	public function loadState(array $params) {
		parent::loadState($params);

		$filters = '';

		foreach($params as $param => $value) {
			if($param == 'action' || $param == 'id') continue;
			if(!is_array($value)) {
				$filters .= '&' . $param . '=' . $value;
			} else {
				foreach($value as $k => $v) {
					$filters .= '&' . $param . '[' . $k . ']=' . $v;
				}
			}
		}

		$this['paginator']->filters = $filters;

	}
?>

EDIT: ještě je třeba dodělat kontrola, jestli jsou ty parametry v tom formuláři nebo to raději brát přímo z toho formu.

Editoval joe (20. 2. 2011 16:06)

joe
Člen | 313
+
0
-

Ještě poznámka – je na to ale třeba myslet při routách, kdyby se ten parametr paginator-page nějak zkracoval, tak aby ty filtry začínaly otazníkem.

Nebo vás napadne ještě nějaké jiné / lepší řešení jak toho docílit?

Díky moc za odpovědi

EDIT: Mi neříkejte, že ještě nikdo neřešil něco podobného…

Editoval joe (20. 2. 2011 22:51)