Naplnění persistentního parametru komponenty ze session a generování odkazu

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

Dobrý den,
řeším komponentu na přehled položek, u které je i filtr vyhledávání. Pomocí persistentních parametrů komponenty přenáším pole filtrovaných hodnot. Chtěl bych ale umožnit i to, že po opuštění stránky s touto komponentou, se při návratu na ní, zobrazí poslední nastavený výběr.
Vyřešil jsem to tím, že si pole filtrovaných hodnot ukládám i do session, pokud se ke komponentě vrátím a má prázdný persistentní parametr, načtu jej ze session.
To funguje v pořádku.

Problém je v tom, že komponenta je „ajaxová“ a mám zde tedy tlačítko (odkaz) které generuje pouze url na aktuální stránku (aby se mohl odkaz výběru někomu třeba přeposlat). Tlačítko je v šabloně komponenty datagridu.

<a href="{link this}">Aktuální výběr</a>

Jenže při tomto prvním požadavku, kdy je persistentní parametr prázdný a naplním jej tedy obsahem ze session (toto provádím v attached metodě komponenty), se odkaz tlačítka vygeneruje bez persistentních parametrů.

Napadlo mě že k tomu by se dala použít metoda saveState(), ale to se mi nepodařilo nijak rozchodit.

Zjednodušená ukázka kódu.

<?php
class ItemsList extends \Nette\Application\UI\Control
{
	/** @persistent */
	public $filter = [];

	protected function createComponentItemsDatagrid()
	{
		//...
	}

	protected function createComponentFilters()
	{
		//...
	}

	public function handleFilter( $name, $value )
	{
		$this->filter[$name] = $value;

		//testovací přístup k session
		$this->getPresenter()->getSession('test')->filter = $this->filter;

		if($this->getPresenter()->isAjax()){
			$this->redrawControl('wrapper');
		}
		else {
			$this->redirect('this');
		}
	}

	protected function attached( $presenter )
	{
		parent::attached($presenter);
		if ($presenter instanceof \Nette\Application\UI\Presenter) {
			if(isset($presenter->getSession('test')->filter)){
				$this->filter = $presenter->getSession('test')->filter;

				//tady by asi mělo něco být
			}
		}
	}
}
?>

Za každé nakopnutí děkuji.

Editoval jEhLa (5. 9. 2016 14:31)

artemevsin
Člen | 61
+
0
-

tohle chování mi přijde trošku zbytečné – nebylo by jednodušší mít na stránce nějaký box typu Poslední hledání, kde se bude vypisovat posledních 5 vyhledávání? Přece jen když znovu otevřu nějakou stránku,tak neočekávám, že mi tam zůstane aktivní filtr, je to až trošku matoucí, uživatel si toho ani nemusí totiž všimnout…

jEhLa
Člen | 70
+
0
-

@artemevsin Zbytečné to možná je. Ale filtr obsahuje docela dost možností. A také se z této stránky bude často odcházet pryč. Tak by bylo nepraktické, aby pokaždé když se uživatel vrátí, toto musel znovu nastavovat, nebo jen vybírat z historie.

artemevsin
Člen | 61
+
0
-

tak zkus se podívat spíše na metodu loadState() . Metoda nastavuje persistentní parametry presentrů. saveState() dělá opak – naplní pole hodnotami persistentních parametrů.
Zdrojak toho řekne víc :)

jEhLa
Člen | 70
+
0
-

Ani s loadState() se mi to nepodařilo rozchodit. Stále stejné chování.
Funguje to pouze pokud tu proměnnou $filter, naplním ze session přímo v presenteru. Pak to funguje. Pokud se o to snažím v komponentě, tak se persistentní parametry negenerují.

Takhle to funguje, ale musím to dělat z presenteru :-/

<?php
class ItemsList extends \Nette\Application\UI\Control
{
    /** @persistent */
    public $filter = [];

	public $onFilter;
	//...

    public function handleFilter( $name, $value )
    {
		//...

		$this->onFilter( $this->filter );

		//...
    }
}

class DefaultPresenter extends BasePresenter
{
	public function renderDefault()
	{
		$control = $this['itemsList'];
		if(!count($control->filter) and isset($this->getSession('test')->filter)){
			$control->filter = $this->getSession('test')->filter;
		}
	}

	protected function createComponentItemsList()
	{
		$control = new ItemsList($this->model);
		$control->onFilter[] = function($filter){
			$this->getSession('test')->filter = $filter;
		};
		return $control;
	}
}
?>

Editoval jEhLa (7. 9. 2016 19:24)

artemevsin
Člen | 61
+
0
-

ale vždyť to můžeš přesunout do komponenty:

<?php
class ItemsList extends \Nette\Application\UI\Control
{
    /** @persistent */
    public $filter = [];

    public $onFilter;
    //...

	public function __construct(Session $session)
	{
		$this->session = $session->getSection('itemsListFilter');
		if(!$this->filter && $this->session->filter){
            $this->filter = $this->session->filter;
        }
	}

    public function handleFilter( $name, $value )
    {
        //...
		$this->session->filter = $this->filter;
        //...
    }
}
?>

Přinejmenším to má výhodu v tom, že v komponentě přiznáváš závislost na session. Pak už stačí jen vytvořit továrničku, která se o ty závislosti postará.

<?php

interface IItemsListFactory {
	/**
	 * @param \Nette\Http\Session $session
	 * @return ItemsList
	 */
	public function create(\Nette\Http\Session $session);
}

?>

Zaregistrovat továrničku jako službu a do presenteru injectnout továrničku. Takže v presenteru pak budeš mít:

<?php

class DefaultPresenter extends BasePresenter
{
    public function renderDefault()
    {

    }

    protected function createComponentItemsList()
    {
		$control = $this->itemsListFactory->create();
        return $control;
    }
}

?>
CZechBoY
Člen | 3608
+
+2
-

@artemevsin jen tě opravím, že v továrničce právě ten parametr $session uvádět nesmíš – jinak by si musel session předávat přes presenter/jinou komponentu.

interface IItemsListFactory
{
    /**
     * @return ItemsList
     */
    public function create();
}
artemevsin
Člen | 61
+
0
-

No jasný, moje chyba :)

jEhLa
Člen | 70
+
0
-

Děkuji za odpovědi.

Takhle jsem to chtěl původně řešit, předat jen závislost na session.
Ale zapomněl jsem podotknout, že tato komponenta bude použita na více místech, a nechci aby měli tu session stejnou (sdílenou) chtěl jsem si nastavit sessionSection až podle názvu komponenty a toho kde bude vytvořená. A to mohu provést až po attached, a né v constructoru. :-/

Ale můžu komponentě předat sessionSection z presenteru generovanou jen podle názvu presenteru a aktuální action. To by mělo stačit.

Stejně mi ale vrtá hlavou proč to nejde udělat právě až v metodě attached :-/

Editoval jEhLa (8. 9. 2016 17:38)

CZechBoY
Člen | 3608
+
0
-

@jEhLa Moc dobře nechápu co teď řešíš… předáš si Session a z ní si vytáhneš SessionSection jakou potřebuješ.

public function __construct(Session $session)
{
	$this->session = $session;
}

public function attached($component)
{
	parent::attached($component);

	$this->sessionSection = $this->sesion->getSection($this->getUniqueId());
}

protected function createComponentForm()
{
	$form = ...;
	$form->setValues($this->sessionSection['values']);
	return $form;
}
jEhLa
Člen | 70
+
0
-

@CZechBoY Ano, tohle funguje u formuláře.
Ale řeším to, že pokud v metodě attached, když už vím jakou sessionSection použít. Vytáhnu si zni data, nastavím je do persistentního parametru, tak když chci vygenerovat odkaz na aktuální stránku {link this}, tak se vygeneruje odkaz jako by byl tento persistentní parametr prázdný. I když si ten persistentní parametr potom dumpnu, tak prázdný není, ale vygenerovaný odkaz jej neobsahuje.

Editoval jEhLa (17. 9. 2016 15:31)