Grido – DataGrid pro Nette

Tirus91
Člen | 199
+
0
-

@o5
Prosím tě, jak mohu vypnout ajax (v JS jsem uvedl false, ale i tak to blbne) a jak mohu zaměnit datepicker za svůj vlastní? Když jsem si v Date.php zaměnil jen tu třídu ‚date‘ za ‚datepicker‘ tak sice můj kalendář se vyvolal, ovšem následně po kliknutí mimo nbeo na jiný prvek mi ta hodnota zmizela (v jiném formuláři/komponentě než GRIDO mi to funguje).

protected function getFormControl()
    {
        $control = parent::getFormControl();
        $control->getControlPrototype()->class[] = 'datepicker';
        $control->getControlPrototype()->attrs['autocomplete'] = 'off';

        return $control;
    }
Pavel Kravčík
Člen | 1195
+
0
-

@o5: Nešlo by přidat něco na způsob:

$grid->setHeaderRowCallback('buttons');

Rád bych si trochu upravil tlačítka Vyhledat a Reset (http://imgur.com/nxjlgLo). Přes lokalizaci můžu měnit popisek, ale do value="" ikonku nedostanu.

Nebo mi napadá ještě změnit {formContainer buttons}{/form} na manuální vykreslování a místo input použít <button>sem vypsat lokalizaci</button>.

Desttro
Člen | 126
+
0
-

Je nějak možno nastavit barvu řádku? Modifikuji si jednotlivé collums pomocí setCustomRender, ale rád bych, když někdo upraví zazánam, aby ten celý řádek změnil styl – třeba barvu pozadí.

Děkuji

Pavel Kravčík
Člen | 1195
+
0
-

@Desttro: http://o5.github.io/…tion.cs.html#…

$grid->setRowCallback(function($row, Html $tr)
{return $tr});
Desttro
Člen | 126
+
0
-

kzk_cz napsal(a):

@Desttro: http://o5.github.io/…tion.cs.html#…

Děkuji, příště si projedu dokumentaci kompletně celou.

o5
Člen | 416
+
0
-

kzk_cz napsal(a):

Nebo mi napadá ještě změnit {formContainer buttons}{/form} na manuální vykreslování a místo input použít <button>sem vypsat lokalizaci</button>.

Můžeš použít vlastní šablonu, kde překryješ blok action a vypíšeš si vlastní formulářové prvky. Budeš ale muset asi překrýt i metodu Grid:createComponentForm aby to nebyl input ale button.

{extends path/to/vendor/grido/Grid.latte}

<th class="buttons" n:block="action">
    {formContainer buttons}
        {input search, title => 'Použít filtr', value => '↳', class => 'btn btn-default btn-xs'}
        {input reset, title => 'Resetovat filtr', value => '⟲', class => 'btn btn-default btn-xs'}
    {/formContainer}
</th>

S tím button[type=submit] se to chovalo různě napříč browsery, když jsem to kdysi zkoušel.

Pavel Kravčík
Člen | 1195
+
0
-

Díky vyzkouším, tohle jsem neznal.

Desttro
Člen | 126
+
0
-

Podporuje Grido SubGridy? např: http://www.grid.jakubholub.cz/subgrid

Editoval Desttro (18. 2. 2015 10:54)

Ja
Člen | 260
+
0
-

Ted jsem vyresil jednu vec, ale vrta mi hlavou, zda to jde i jinak.
Mam dve stranky:

  1. SEKCE (SectionPresenter)
  2. STRANKY (PagePresenter)

STRANKY spadaji pod prave 1 SEKCI

Mam grido na vypis sekci a chci se z neho prokliknout na Page:default,id_section=X

Ackoli mam definovano v PagePresenter:

<?php
	public function actionDefault($id_section){}
?>

tak mi to grido stale preklada na odkaz /page/?id=X… (id odpovida sloupci ID v SECTION tabulce). Da se prinutit, aby smeroval na jiny presenter i s takto pozmenenym nazvem parametru?

bazo
Člen | 620
+
0
-

$id_section !== $id, nazvy parametrov musia byt rovnake

Jan Mikeš
Člen | 771
+
0
-

Ahoj, nechce mi fungovat suggestions nad text columnem s doctrine data sourcem.
S doctrine jsem zacal dost cerstve, nevim tedy jestli se jedna o chybu na me strane nebo bug v gridu – prosim o nakopnuti.

InvoicingData service – ziskani QB:

	public function getUserInvoicingData(Entities\User $user)
	{
		return $this->invoicingDataDao->createQueryBuilder("a")
			->where("a.user = :user")
			->setParameter("user", $user);
	}

Grid:

	protected function createComponentGrid($name)
	{
		$grid = new Grido\Grid($this, $name);
		$grid->translator->setLang("cs");
		$grid->filterRenderType = Grido\Components\Filters\Filter::RENDER_INNER;

		$grid->setModel($this->invoicingData->getUserInvoicingData($this->user->getIdentity()));
		$grid->setDefaultSort(["name" => "ASC"]);

		$grid->addColumnText("name", "Subjekt")
			->setSortable()
			->setFilterText()
				->setSuggestion();

		return $grid;
	}

Pokud odstranim ->setSuggestion() vse funguje OK (i filtrovani, i razeni…). Pokud ale zacnu psat do filtru a chvilku pockam (je tam rekl bych timeout na event, nez se odpali dotaz do db..), tak se objevi exception

Doctrine\ORM\Query\QueryException
[Semantical Error] line 0, col 116 near ‚name ASC‘: Error: ‚name‘ is not defined.

Vygenerovane DQL:

SELECT DISTINCT a.name
FROM App\Model\Entities\InvoicingData a
WHERE a.user = :user AND a.name LIKE :ssxe0
ORDER BY name ASC

Rekl bych, ze v DQL chybi a. pred name v ORDER BY klauzuli.

o5
Člen | 416
+
0
-

@Lexi: Můžeš se podívat co za DQL leze v grido-examples v DoctrinePresenter-u?

Jan Mikeš
Člen | 771
+
0
-

@o5: tak jsem zjistil, ze u sebe mam v composeru dev-master verzi a v examples je 2.0.8, pokud udelam composer update v examples, tak se to rozbije i tam, v 2.0.8 je vse ok :-) tim padem vyreseno, nebudu pouzivat master verzi

o5
Člen | 416
+
+1
-

@Lexi: Divný, v masteru jsou navíc dva commity týkající se doctrine. Mohl bys prosím zkusit zjistit, kterej z nich to rozbíjí a založit issues na Githubu. Dík!

EDIT: Tak to bude asi tahle změna.

Editoval o5 (24. 2. 2015 14:58)

pepakriz
Člen | 246
+
+1
-

@o5 Ano, mělo by tam být ->orderBy($qb->getRootAlias() . '.' . $column);

o5
Člen | 416
+
0
-

@Lexi: Zkus to prosím s aktuálním masterem. Mělo by to být fixnuté.

Editoval o5 (24. 2. 2015 15:15)

Jan Mikeš
Člen | 771
+
0
-

@o5: vyborne, jiz vsechno funguje tak jak ma, Diky!

raketoplan2005
Člen | 147
+
0
-

Ahoj,

mám např. tabulku objednávek s joinem do tabulky projektů. Sloupec z joinované tabulky si v selectu pojmenovávám např. jako project_name.

V gridu pak zobrazuji přehled objednávek a k nim i název projektu, ke kterému se váže.

Ve sloupci s názvem projektu bych chtěl ale nejen povolit filtr (setFilterText() s setColumn('project.name') funguje), ale také našeptávat. Tuto kombinaci ale neumím zprovoznit.

Grido mi totiž pro suggestion udělá:

SELECT DISTINCT project_name FROM...

což narazí na to že project_name neexistuje. Když do ‚setSuggestion()‘ přidám setSuggestion('project.name') tak skončím naopak s:

Undefined property: DibiRow::$project.name

Jak bych měl prosím toto řešit správně? Děkuji

Editoval raketoplan2005 (26. 2. 2015 16:28)

pepakriz
Člen | 246
+
0
-

@raketoplan2005 V druhém případě když použiješ SymfonyPropertyAccessor, tak ti to možná bude fungovat.(viz https://github.com/…Accessor.php)

raketoplan2005
Člen | 147
+
0
-

@pepakriz :

Děkuji za rady, nevím jestli jsem udělal vše co je nutné, ale přes composer jsem stáhl symfony/property-access a přidal jsem do komponenty

$propertyAccessor = new \Grido\PropertyAccessors\SymfonyPropertyAccessor();
$grid->setPropertyAccessor($propertyAccessor);

Vše se chová stejně a to takto:

  • pokud mám jen filtr bez našeptávání se setColumn('project.name') tak to filtruje správně
  • pokud mám jen filtr s našeptáváním setSuggestion('project.name') a obecným setColumn(), tak to data pro suggestion hledá správně DISTINCT project.name ale vypadnu na filtrku, protože ve WHERE je to jen name LIKE %foo% – Column 'name' in where clause is ambiguous
  • pokud mám setColumn('project.name') a setSuggestion('project.name') pohromadě tak končím s Undefined property: DibiRow::$project.name

Editoval raketoplan2005 (26. 2. 2015 16:59)

o5
Člen | 416
+
0
-

@raketoplan2005: Změna PropertyAccess to neřeší, koukni do grido-examples, tam se našeptává sloupec country který je najoinovaný.

raketoplan2005
Člen | 147
+
0
-

@o5:

Když to zkusím na demu tak je tam asi bohužel stejný problém (500).

o5
Člen | 416
+
0
-

@raketoplan2005: Sorry za nečinnost. V demu byla chyba (opraveno), v testech to bylo správně :)

raketoplan2005
Člen | 147
+
0
-

@o5:
Fajn, děkuji tohle zafunguje. Ale co v případě, když joinuji dvě tabulky, např. projekt a společnost?

$grid->addColumnText('project_name','Projekt')
		->setSortable()
		->setFilterText()
		->setColumn('project.name')
		->setSuggestion('name');

$grid->addColumnText('company_name','Společnost')
		->setSortable()
		->setFilterText()
		->setColumn('company.name')
		->setSuggestion('name');

To pak narazí na stejně pojmenované sloupce name – Column 'name' in field list is ambiguous. To je taky v Gridu řešitelné, nebo už mě to směřuje na view?

Tirus91
Člen | 199
+
0
-

@o5
Ahoj,
prosím tě, mám pár otázek. Jde nějak upravit šablona pro Grido, aby vypadala nějak následovně?
Obrázek z Admin LTE2

Jde mi hlavně o to stránkování a hromadné akce. U těch akcí že by to nebyl číselník, ale tlačítka nahoře. (místo názvu by byl class). Dále ještě zda jde všechny filtry (cca 4 sloupečky) navázat na jeden input.

Díky, Tirus

o5
Člen | 416
+
0
-

@raketoplan2005: ono to dost dobře automaticky udělat nejde a tedy pro tyto účely je tam metoda setSuggestionCallback().

@Tirus91:

Jde nějak upravit šablona pro Grido, aby vypadala nějak následovně

jde

Dále ještě zda jde všechny filtry (cca 4 sloupečky) navázat na jeden input

jde, přes setColumn(). Stačí se podívat na jeden z examplů.

Editoval o5 (2. 3. 2015 12:20)

Tirus91
Člen | 199
+
0
-

@o5

Mohu gridu defaultně nastavit vlastní latte? nedohledal jsem správnou metodu. Rád bych si změnil ten vzhled, ale nechci šahat do latte šablony, kterou mi composer může přepsat.

na example kouknu. Díky

Pavel Kravčík
Člen | 1195
+
0
-

Pár příspěvků nahoře…

{extends path/to/vendor/grido/Grid.latte}
Tirus91
Člen | 199
+
0
-

kzk_cz napsal(a):

Pár příspěvků nahoře…

{extends path/to/vendor/grido/Grid.latte}

ouch. to jsem pochopil, že to je přetížení jenom. Ale děkuji

Pavel Kravčík
Člen | 1195
+
0
-

@Tirus91: A ty to chceš změnit celé? Proč nezkusit něco podobného:

class MyGrid extends \Grido\Grid
{
	/**
	  *	Override
      * @see parent
	  */
	public function createTemplate($class = NULL)
    {
        $template = parent::createTemplate($class);
		$template->setFile(__DIR__ .'JardaJagr.latte');

		return $template;
	}
}
o5
Člen | 416
+
0
-

@kzk_cz jednodušší je to přes $grid->setTemplateFile().

Pavel Kravčík
Člen | 1195
+
0
-

@o5: Moje chyba. Nečekal jsem, že tam něco takového bude, když se na to ptal. :)

Šaman
Člen | 2659
+
0
-

Mohu poprosit o přidání unikátní třidy na tlačítko exportu? Rád bych si ho nastyloval po svém, ale teď není jak ho jednoznačně určit.
Pokud bys tomu chtěl věnovat maličko víc času, tak samozřejmě by se takové identifikační třídy hodily i na další prvky.
Díky.

o5
Člen | 416
+
0
-

@Šaman máš to v masteru.

takové identifikační třídy hodily i na další prvky

konkrétně?

Pavel Janda
Člen | 977
+
0
-

Zdravíčko,

@o5
na Nette 2.3 blbnou v Grid.latte nějaká malá písmenka Presenterů (zmiňovaný BC break).

User Warning

Case mismatch on presenter name 'Front:story', correct name is 'Front:Story'.

Lajna 165
164:    <?php $iterations++; } if ($showActionsColumn) { ?>                <td class="actions center">
165:    <?php $iterations = 0; foreach ($actions as $action) { if (is_object($action)) $_l->tmp = $action; else $_l->tmp = $_control->getComponent($action); if ($_l->tmp instanceof Nette\Application\UI\IRenderable) $_l->tmp->redrawControl(NULL, FALSE); $_l->tmp->render($row) ;$iterations++; } if (!$actions) { ?>
166:                            &nbsp;

Edit:

Omluvám se, blbost je v kódech dotyčného.

Editoval Beton (5. 3. 2015 14:13)

kolsi
Člen | 131
+
0
-

Lze u Grida nějak elegantně přizpůsobit šířku sloupce obsahu?

Řekněme, že mám Grido, které obsahuje více sloupců, z nichž některý obsahuje třeba jenom ANO/NE. Přesto tento sloupec dostane větší šířku, která způsobí, že jiný sloupec obsahující delší obsah se zalomí a výsledek pak nevypadá pěkně. Kdyby ten krátký sloupec se automaticky přizpůsobil, tak by k zalomení vůbec nedošlo.

o5
Člen | 416
+
0
-

kolsi napsal(a):

Lze u Grida nějak elegantně přizpůsobit šířku sloupce obsahu?

Nyní to bohužel jinak, než že si nastavíš šířku ručně u každého sloupce nejde. Já nejsem moc dobrý kodér, takže pokud víš, jak se to má řešit elegatně a chceš přispět, pošli pull request.

kolsi
Člen | 131
+
0
-

Já to teď právě měnim pomocí getHeaderPrototype()->style[] = „width: 1%“; ale chtěl jsem vědět, jestli třeba někdo nemá něco hezčího. Možná by jenom stačilo přidat metodu setWidth, která to udělá za mě.

Šaman
Člen | 2659
+
0
-

o5 napsal(a):

@Šaman máš to v masteru.

takové identifikační třídy hodily i na další prvky

konkrétně?

Ahoj, díky. Konkrétně jsem na to teď koukal a pokud se rozdělí class count pro paginátor od exportu, tak to bude v pohodě.
Ještě řeším jeden problém – mám v zadání, že tlačítko pro export má být mimo grid. Ok, vytvořil jsem si ho a odkazuji se na akci gridu pomocí n:href="personGrid-export-export!" Problém je, že takovýto odkaz nezohledňuje filtry. Dokonce ani když jsem se zkušel hacknout Grido a vypsat si odkaz přímo z něho, tak se mi filtry ignorovaly. Začínám mít dojem, jestli ty filtry (jejich dodání do adresy) nejsou v režii nějakého JS.
Můžeš mě kopnout správným směrem, pokud chci tlačítko umístit mimo grid, pls?

mpis
Člen | 65
+
0
-

Měl bych taky jeden dotaz související s filtry.
V gridu mám vyfiltrovaný seznam.
Jdu na editaci jednoho řádku a po návratu z editace se
mi zobrazí všechny záznamy.
Jak docílím toho, aby se mi po návratu zobrazily záznamy podle toho předchozího filtru?

o5
Člen | 416
+
+1
-

@Šaman: takhle ti to půjde:

$this['grid']['export']->link('export!')

@mpis: jestli máš editaci v rámci jednoho presenteru, tak by mělo fungovat nastavit komponentu grido jako perzistetní. Taky by ti mohlo pomoci $grid->setRememberState().

mpis
Člen | 65
+
0
-

@o5:
Díky. Funguje obojí.
Jen bych měl, z důvodů čistě studijních, otázku na nettí puristy.
Co je čistší? Persistentní komponenta nebo $grid->setRememberState()?

o5
Člen | 416
+
0
-

mpis napsal(a):

Co je čistší? Persistentní komponenta nebo $grid->setRememberState()?

To IMHO nejde takto jednoznačně říci. Pokud ta editační akce je v rámci stejného presenteru, tak to stačí vyřešit persistetní komponentou. Já ale všude, kde mám nasazené Grido, nastavuju ukládání stavu do sessions jako výchozí, protože je to prostě více uživatelsky přívětivé.

Šaman
Člen | 2659
+
0
-

o5 napsal(a):

@Šaman: takhle ti to půjde:

$this['grid']['export']->link('export!')

Bohužel, tohle mi taky odřízne nastavení filtrů. Nicméně provedl jsem pár pokusů a problém je někde v AJAXu. Když ho vypnu (zakomentuji načtení nette.ajax.js), tak to funguje. To jsem si myslel už v noci, že základni request je bez filtrů, ty přidá až AJAX a nějak pomocí JS je doplní do adresy, aby fungovall refresh. Ale při tvorbě odkazů mimo komponentu ty filtry asi nejsou vidět…

sKopheK
Člen | 207
+
0
-

Ahoj, mám problém se zpracováváním hromadných operací pro označené řádky gridu.
Při provádění překreslení přes AJAX se Grid nepřekreslí a jsou v něm vypsaná stejná data jako před provedením operace. Postupoval jsem stejně jako v příkladech (kde se však fyzicky žádná operace neprovádí, jen se zobrazí flash zprávička – to mám v pořádku). Poradíte, jak refreshovat grid přes AJAX, abych nemusel dělat refresh přes redirect?

Jan Mikeš
Člen | 771
+
0
-

Zacinam byt fakt zoufaly, urcite se jedna o nejakou prkotinu a ja to jen prehlizim… Presne podle grido examples zkousim pro Doctrine datasource vypsat sloupec z propojene entity:

$model = new \Grido\DataSources\Doctrine(
            $this->entityManager->getDao(Entities\Order::class)->createQueryBuilder('a')
                ->addSelect('c')
                ->innerJoin('a.customer', 'c'),
            array('firstname' => 'c.firstname'));
        $grid->model = $model;

		$grid->addColumnText("firstname", "Jméno");

Je to prakticky copypaste primo z examples, nebo delam neco spatne?

Dostavam Kdyby\Doctrine\MemberAccessException
Cannot read an undeclared property App\Model\Entities\Order::$firstname

	// Order.php
	/**
	 * @ORM\OneToOne(targetEntity="Customer", inversedBy="order")
	 */
	private $customer;

	public function setCustomer(Customer $customer)
	{
		$this->customer = $customer;
		$customer->setOrder($this);
		return $this;
	}

	public function getCustomer()
	{
		return $this->customer;
	}

Vazne uz nevim co delam spatne…

Toto mi pro vypis funguje, ale jakmile zacnu pridavat sort/filter/suggestion, pak dostavam ruzne exceptions, takze tusim ze to neni spravny pristup.

	$grid->addColumnText("customer.firstname", "Jméno");

Jak tedy spravne pridat sloupec z propojene entity?

VK
Člen | 10
+
0
-

Zkus

$model = new \Grido\DataSources\Doctrine(
	$this->entityManager
		->getRepository(Entities\Order::class)
		->createQueryBuilder('a')
        ->addSelect('c')
        ->innerJoin('a.customer', 'c'),
    [
		'customer.firstname' => 'c.firstname'
	]
);

$grid->addColumnText("firstname", "Jméno")
	->setColumn('customer.firstname');

Včera jsem zrovna dělal něco podobného s translation tabulkami (translatable) a vše funguje.

Editoval VK (13. 3. 2015 10:06)

kolsi
Člen | 131
+
0
-

Měl bych jenom takový drobný návrh na vylepšení, které ušetří pár řádků kódu.

Mám kód (pouze schématicky napsáno):

$grid->addActionEvent('delete', 'Delete', function($id) {
	if(mám oprávnění smazat položku $id) {
		delete $id;
	}
})->setDisable(function($item) {
	return !(mám oprávnění smazat položku $item->id);
});

A musím tam tu kontrolu oprávnění psát dvakrát. Myslím, že by bylo logičtější, že pokud daný řádek má akci zakázanou (v setDisable), tak by nemělo dojít ani k zavolání callbacku této akce (např. pokud podstrčím ID do URL). Je to jenom drobnost, ale může se hodit :-)

Editoval kolsi (13. 3. 2015 11:14)

o5
Člen | 416
+
0
-

kolsi napsal(a):

A musím tam tu kontrolu oprávnění psát dvakrát.

Co takto? :)

$isDeletable = function($id) {
	return $id === "XX";
};
$grid->addActionEvent('delete', 'Delete', function($id) use ($isDeletable) {
    if($isDeletable($id)) {
        delete $id;
    }
})->setDisable(function($item) use ($isDeletable) {
    return !$isDeletable($item->id);
});
Jan Mikeš
Člen | 771
+
0
-

@VK toto funguje, jenze v Doctrine demu je ukazano, ze jako alias muzu pouzit nazev bez nutnosti tecky, netusim v cem je tedy problem, jestli to neni tim, ze v demu existuje property entity „country“ ke ktere se pak vytvari sloupec, ale pouze se ji mapuje hodnota jinam? V tom pripade jde mozna o bug?

edit: anyways diky, muj problem to vyresilo, neni az tak dulezite jestli pisu „customer.firstname“ nebo „firstname“

Editoval Lexi (13. 3. 2015 12:35)