ublaboo/datagrid: mocný, rychlý, rozšiřitelný, hezký, anglicky dokumentovaný datagrid

Pavel Janda
Člen | 977
+
0
-

@Attanon Blok chevron-cion (s překlepem) jsem přejmenoval na {block icon-chevron} a přidal jsem některé další blocky:

{block icon-sort} // Without order clause
{block icon-sort-up} // ASC
{block icon-sort-down} // DESC
{block icon-caret-down} // Ikonka, která by-default je stejná jako sortící, ale je v dropdownu u skrývání sloupců

Editoval Pavel Janda (11. 3. 2016 8:18)

Pavel Janda
Člen | 977
+
0
-

@sepo Zakládám issue a bude to ve verzi 2.4

Edit: Je to v masteru, kdybys byl nedočkavý. :)
Edit 2: Tak ne v2.4, ale v3. Počítám s otagováním dalších fíčur, asi 2 z nich obsahují možný BC brejčík.

Editoval Pavel Janda (11. 3. 2016 10:05)

Bedna
Člen | 7
+
0
-

Pavel Kouřil napsal(a):

Hmm, tak jsem jej konečně pořádně otestoval a použil, upravil si šablony aby seděly k šabloně (metronic) a datepickeru co používáme, a narazil jsem na následující:

  • dají se nějak líp změnit šablony filtrů než si dělat vlastní fork? je to popravdě docela nepraktické
  • pamatování filtrů a řazení v session je supr věc, ale v momentě co má člověk více datagridů, skládá to queries špatně; lze nějak nastavit název datagridu který by sloužil jako session „namespace“ a tedy každý datagrid by měl vlastní „paměť“?
  • jak dostat přes vlastní render do sloupce HTML?

Jinak skvělá práce!

PS: Doporučoval bych vypsat závislosti i jinak než v boweru (stačí linky na knihovny v README/dokumentaci), ať člověk může datagrid jednoduše použít i bez něj. Nejhorší bylo dohledání happy.js. :)

Jen aby zas někdo nepátral 2 hodiny jako já https://github.com/…ljanda/happy

A ještě dotaz, proč nefunguje confirm v tomto případě:

$this->addAction("remove", "", "deleteCategoryGrid!")
                ->setClass("delete btn-icon")
                ->setConfirm("Určitě chcete odstranit kategorii %s ?", "name");

a v tomto ano:

$this->addAction("remove", "", "deleteCategoryGrid!")
                ->setClass("delete btn-icon ajax")
                ->setConfirm("Určitě chcete odstranit kategorii %s ?", "name");

Editoval Bedna (11. 3. 2016 12:44)

Pavel Janda
Člen | 977
+
+1
-

@Bedna Hm, confirmation dialog je navázaný na ajax requesty. Do příští verze to rozšířím a pravděpodobně i prefixnu: data-confirmdata-datagrid-confirm.

majky358
Člen | 37
+
0
-

Nazdar,

mám menší problém pri väzbách, konkrétne využívam LeanMapper.

Príklad:

Entita User:
@property UserRole $userRole m:hasOne (user_role_id:user_role)

Entita UserRole:
@property string $title (user_role_title)

Ako datasource gridu nastavujem LeanMapper\Fluent → DibiFluent.
A pridávam stĺpec:

$this->addColumnText("user_role_id", "userRoleTitle", "user_role_id.user_role_title");

Čo mi hlási undefined row v DibiRow. Ak by sa našla rada, bol by som veľmi vďačný ak niekde robím chybu :) Skúšal som aj meniť názvy pre stĺpce ale bez výsledku.. Alebo musím vytvárať ručne join, ten keď napíšem do dotazu funguje.

Editoval majky358 (12. 3. 2016 0:52)

Pavel Janda
Člen | 977
+
0
-

@majky358 Jelikož neexistuje žádná oficiální LeanQuery/LeanMapper QueryObject, tak při použití LeanMapperu vlastně v datagridu LeanMapper vůbec nevyužiješ (pakliže si tedy nenaimplementuješ vlastní DataSource). Pro datagrid tedy použiješ jen tu Dibi\Fluent (== DibiFluent). Můžeš mi ukázat, jak tu fluent query vytváříš?

majky358
Člen | 37
+
0
-

@PavelJanda

LeanMapper\Repository:

$this->connection->select('*')->from($this->getTable()); //user table

LeanMapper\Repository->createFluent() som skúšal tiež…

funguje ok:

$this->connection->select('user.*, user_role.user_role_title')
->from($this->getTable())
->leftJoin('`user_role` ON `user.user_role_id` = `user_role.id`');

Editoval majky358 (12. 3. 2016 17:15)

Pavel Janda
Člen | 977
+
+1
-

@majky358 Takže ok? :)

Ano, první query nemůže fungovat, když nemá joinutou potřebnou tabulku..

Arty
Člen | 3
+
0
-

Řeším teď něco podobného. Používám jako DataSource LeanMapper entitu. Zobrazování funguje pěkně i přes cizí klíče. Problém ovšem nastane pokud chci podle nějakého sloupce řadit:

File: ...\datagrid\src\DataSource\ArrayDataSource.php:143
Cannot use object of type Namespace\ORM\Entity\Page as array

Je tam vubec širší podpora pro Entity nebo musím používat DibiFluent? S tím mi vše funguje, ale zase pro mě postrádá ORM smysl.

Díky

Pavel Janda
Člen | 977
+
0
-

@Arty Hmmm. No, ještě mě nenapadlo dávat LeanMapper\Entity do pole a to dávat jako datasource datagridu.

Tady je problém lehce filozofický.

Aby byly pořádně podporované LeanMapper\Entity, byl by třeba nějaký wrapper kolem DibiFluent. Stejně, jako má doctrina QueryBuilder. Bohužel, na LeanQuery se tharos vykašlal (pravděpodobně málo času). Pak tu byl někde pokus o LeanMapper\QueryObject, ale to není ofiko a taky nevím, jak je to s vývojem.

Můj postoj k tomuto problému: Jelikož se tu někde psalo, že na to tharos nemá čas a taky že tharos předal vývoj @castamir -ovi, nejlépe pingnout právě jeho. LeanMapper nepovažuji za mrtvý.

Na projektech, kde pracuji s LeanMapperem, používám jako datasource Dibi\Fluent.

Pavel Janda
Člen | 977
+
0
-

@Arty Asi nejrychlejší řešení z mé strany je přidat Property Access (u věcí ORM) do ArrayDataSource. Dám to do issue.

Arty
Člen | 3
+
+1
-

Jasný, děkuji za odpověď. Nemám zatím problém používat DibiFluent, jen je to škoda :)
Jinak Grid je moc hezký a fandim mu!

Dark0ne
Člen | 47
+
0
-

Ahoj,
chtěl jsem se zeptat, jak nastavit, aby se zobrazovala správně diakritika u exportovaných CSV souborů?

Pavel Janda
Člen | 977
+
0
-

@Dark0ne :) Zobrazuje se „správně“. Pokud bys chtěl změnit kódování výstupu například na windows-1250, tak pls založ ticket na githubu, já tam přidám možnost volby. (Momentálně se nedá měnit kódování předávané v konstruktoru třídě CsvResponse)

Editoval Pavel Janda (14. 3. 2016 17:20)

Dark0ne
Člen | 47
+
0
-

Ahoj,
mám další dotaz :o).
Potřebuji udělat takový výpis, který by uměl na první pohled odlišit dvě kategorie záznamů.

$grid->addColumnText('prijem_vydaj', 'Příjem/Výdaj')
           ->setSortable()->setReplacement([1 => 'Příjem', 0 => 'Výdaj']);

Takto nějak vypadá prvek, který mi odlišuje dané sloupce. Jedná se tedy o dvě kategorie „Příjem“ a „Výdaj“. Já bych potřeboval ideálně barevně odlišit celé řádky dané tabulky jinou barvou (případně jen buňku).
Jednoduše řečeno, aby například řádek výpisu, který je „Příjem“ měl zelenou barvu pozadí a řádek, který je typu „Výdaj“ byl třeba červený. Doufám, že je to pochopitelné. Zkoušel jsem nějak čarovat s proměnnou v setClass, ale ta není podporovaná v addColumnText.

Díky za případné rady :)

Pavel Janda
Člen | 977
+
+1
-

@Dark0ne Pro Row můžeš použít callback a nastavit class podle $item (celému <tr>). Je to nědke v docu…

http://ublaboo.paveljanda.com/datagrid/row – dole „Row callback“

Attanon
Člen | 25
+
0
-

@PavelJanda Dá se udělat u skupinových akcích změna na základě inputu? Že třeba chci změnit všem označeným cenu na určitou hodnotu.?

Pavel Janda
Člen | 977
+
0
-

@Attanon Přemýšlel jsem nad tím, ale zatím jsem to nepotřeboval. Pokud to, použiješ, přidám to třeba do v3.1. Možná 3, uvidíme.

Attanon
Člen | 25
+
+1
-

Pavel Janda napsal(a):

@Attanon Přemýšlel jsem nad tím, ale zatím jsem to nepotřeboval. Pokud to, použiješ, přidám to třeba do v3.1. Možná 3, uvidíme.

Já to určitě využiji. Ale ber to jako vylepšení, které by se šiklo, ale není to nějak důležité, vzhledem k tomu, že jsem první co si o to žádá. Já když budu mít čas, tak zkusím na to mrknout a udělat PR.

castamir
Člen | 629
+
+1
-

Query object pro LeanMapper uz hodne dlouho existuje viz jeho fork

Hodlam ho primo integrovat do LeanMapperu, protoze podle me tam patri. Mimochodem integrace muze vypadat podobne jako z jinem mem rozsireni LeanMapperu

Pavel Janda
Člen | 977
+
+1
-

@castamir Když to integruješ (v nějaké stable verzi), napíšu datasource pro ten query object. Přístup k properties LeanMapper entit už je ready v třídě Row.

Editoval Pavel Janda (15. 3. 2016 22:47)

majky358
Člen | 37
+
0
-

Nazdar, ešte som narazil, keď mám dotaz: (DibiFluent)

$this->connection->select('user.*, user_role.user_role_title')
->from($this->getTable())
->leftJoin('`user_role` ON `user.user_role_id` = `user_role.id`');

Chcem filtrovať nad stlpcom ID (user.id)

$grid->addColumnText("id", "User_ID");
$grid->addFilterText("id", "User_ID");

Vytvorí:

SELECT COUNT(*)
FROM (
SELECT user.*, user_role.user_role_title,
FROM `user`
LEFT JOIN `user_role` ON `user`.`user_role_id` = `user_role`.`id`
WHERE (id LIKE '%12%' COLLATE utf8_general_ci) ) `data`

Nastane problém: Column ‚id‘ in where clause is ambiguous, cez custom where filter by to pravdepodobne išlo riešiť.. nejak elegantnejšie ? Chcel by som zachovať .* Ďakujem, mám xxx tabuliek preto hľadám nejaké riešenie :)

Pavel Janda
Člen | 977
+
0
-

@majky358 Mrkni do docu, metodám DataGrid::addColumnX() a DataGrid::addFilterX() můžeš dávat ještě další parametry. Mimo jiné specifikovat název sloupce: $grid->addFilterText("id", "User_ID", 'user.id');. Textovému filtru můžeš dát dokonce více sloupců, nad kterými bude vyhledávat: $grid->addFilterText("id", "User_ID", ['user.id', 'user.address', 'user.name']);.

Editoval Pavel Janda (17. 3. 2016 7:46)

Pavel Janda
Člen | 977
+
+6
-

Nová verze – v3.0.0

Po nějaké době přišla další verze s nezanedbatelnou porcí nových možností!

Pozor pro ty, kdo používáte Nette\Database: v odděleném vlákně je info o data sourc-u, které umí parsrovat native sql query. Takže už nikdo nemusí fetchovatAll data z databáze, když potřebuje složitější query!

  • Přibyla velká inline editace (dokumentace s ukázkou, demo je nahoře)
  • Skrývání sloupců nyní zobrazuje interaktivní přehled o zobrazených/skrytých sloupcích (demo)
  • Přibylo fluent interface pro přiřazování filtrů sloupcům (jako znáte z jiných gridů – $grid->addColumn()->setFilter(), dokumentace)
  • Přibyly ikonky kalendáře u filtrů, které jsou renderovány v tabulce
  • Je možné nastavit, aby se zresetovala page při změně sortu (DataGrid::setSortableResetPagination())
  • Odstraněna nějaká magie (nelze již volat Column::icon(), ale jen setIcon(), obdobně pro class, title a pod – possible BC break)
  • Další maličkosti/fixy

Díky všem za nápady a spolupráci!

Editoval Pavel Janda (17. 3. 2016 11:28)

sibka
Člen | 24
+
0
-

Chválím verzi 3, velká inline editace je super :-)

Mám jen jeden zádrhel. Po aktualizaci mi nejedou filtry, ale pouze u některých gridů. Nenapadá tě, v čem by mohl být problém?

Pavel Janda
Člen | 977
+
0
-

@sibka To mě nanapadá. Děje se ti to i na ublaboo webu? Zkus smazat komplet cache a session prohlížeče (cokies, assets) a aplikaci. Zkus mi zhruba popsat, kolik a jakých gridů máš na stránce. Já se takovou situaci pokusím nasimulovat.

Blujacker
Člen | 89
+
0
-

Zdravim,

datagrid vypada krasne a uvazuji ze bych ho zacal pouzivat na ukor vlastnimi silami vyvinuteho gridu. Mam ale dotaz, veskere tabulky tahame pres API, takze zadna databaze. Na to by se hodil Array data source. Ale jelikoz nektere tabulky maji tisice radku tak API umi vysledky filtrovat. Slo by tedy datagridu priradit nejaky callback (ktery by prijimal filtry, razeni) pres ktery by se array data source naplnilo?

Dekuji za navedeni, v pripade ze by to slo, grid vyzkousim

Pavel Janda
Člen | 977
+
+1
-

@Blujacker Jistě, je k dispozici interface IDataSrouce, který implementuješ ve své třídě, která bude kecat s vaším API. Abych načrtl ideu:

<?php

/**
*
*/
class ApiDataSource extends IDataSrouce
{

	/**
	 * @var string
	 */
	private $url;

	/**
	 * @var string
	 */
	private $sort_column;

	/**
	 * @var string
	 */
	private $order;

	/**
	 * @var int
	 */
	private $limit;

	/**
	 * @var int
	 */
	private $offset;


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


	public function getCount()
	{
		return file_get_contents($this->url . '?count');
	}


	public function getData()
	{
		return file_get_contents(
			$this->url
			. '?sort=' . $this->sort_column
			. '&order=' . $this->sort_order
			. '&limit=' . $this->limit
			. '&offset=' . $this->offset
		);
	}


	public function limit($offset, $limit)
	{
		$this->offset = $offset;
		$this->limit = $limit;
	}


	public function sort(Sorting $sorting)
	{
		/**
		 * there is only one iteration
		 */
		foreach ($sorting->getSort() as $column => $order) {
			$this->sort_column = $column;
			$this->sort_order  = $order;
		}
	}

}

A pak ten datasource předáš datagridu:

public function createComponentApiGrid($name)
{
	/**
	 * @var Ublaboo\DataGrid\DataGrid
	 */
	$grid = new DataGrid($this, $name);

	$grid->setDataSource(new ApiDataSource('http://my.api'));
}

Editoval Pavel Janda (18. 3. 2016 15:36)

Pavel Janda
Člen | 977
+
+2
-

@Blujacker To je tedy asi nejčistější způsob. Šlo by to klidně i různě obcházet, ale tak bych to vůbec nedělal.

A samozřejmě filtry mohou mít custom callbacky.

PS: Docela se mi ten nápad tahat věci přes apinu zalíbil, nějaký ApiDataSource napíšu – dávat do issues. :D

Editoval Pavel Janda (18. 3. 2016 15:39)

Blujacker
Člen | 89
+
+1
-

Pavel Janda napsal(a):

@Blujacker To je tedy asi nejčistější způsob. Šlo by to klidně i různě obcházet, ale tak bych to vůbec nedělal.

A samozřejmě filtry mohou mít custom callbacky.

PS: Docela se mi ten nápad tahat věci přes apinu zalíbil, nějaký ApiDataSource napíšu – dávat do issues. :D

Dekuji!

Jdu testovat

zoool
Člen | 89
+
0
-

Ahoj jak prosím přepíši dialog confirm? spíše tedy přidat do odkazu data-label aj atributy

Děkuji

Pavel Janda
Člen | 977
+
0
-

@zoool
1, Přesunul jsem jméno toho data atributu datagrid-confirm do statické proměnné. Můžeš si to tedy změnit: Action::$data_confirm_attribute_name = 'my-confirm'.

2, Ve třídě Action je k dispozici metoda ::setDataAttribute(). Nebo jsi myslel jiný odkaz? ColumnLink například?

Pavel Janda
Člen | 977
+
0
-

@Dark0ne Otagoval jsem verzi, kde je možné nastavit kódování CSV exportů a též delimiter. Lze toho nyní dosáhnout jednoduše pomocí dalších parametrů metody DataGrid::addExportCsv[Filtered]():

$grid->addExportCsv(
	'Csv export',
	'examples_all.csv',
	'windows-1250',
	','
);

By default:

$grid->addExportCsvFiltered(
	'Csv export (filtered)',
	'examples.csv',
	$output_encoding = 'utf-8',
	$delimiter = ';'
);

Editoval Pavel Janda (19. 3. 2016 8:46)

Vastlik
Člen | 58
+
0
-

Ahoj,
řeším teď problém, že mám na columnDatetime nastavený renderer, který bere datum a přídá k němu X dní podle toho, jak je ta položka validní. (Př. položka byla vytvořena 1.1.2016, je validní 10 dní ⇒ v datagridu se zobrazí 11.1.2016.) Jenže pokud se snažím použít filterDate, tak to filtruje podle datumu vytvoření, protože je to hodnota, která je v dataSource. Je tu nějaká možnost, jak to udělat krom změnit to datum v datasource?
Díky

zoool
Člen | 89
+
0
-

@PavelJanda
Super děkuji tak to stačí a funguje setDataAtributte. Ještě jsem se chtěl zeptat, zda se dájí nějak globálně nastavit tyto parametry? Nějaké statické pole s těmito parametry nebo chtěl jsem se zeptat jak to řešíš. Děkuji

Pavel Janda
Člen | 977
+
0
-

@zoool Pokud potřebuješ u všech sloupců určitého typu (nebo všech typů) specifické data-atributy, asi bude nejlepší, když si podědíš DataGrid a přepíšeš metody ::addColumn*(). V těch nastavíš svoje data atributy a zavoláš parent::addColumn*(). V továrničkách pak budeš používat ten svůj datagrid.

Pavel Janda
Člen | 977
+
+1
-

@Vastlik Záleží na logice věci. Takhle z patra mě napadají dvě možnosti:

1, Již v dotazu na databázi si můžeš například v mysql posílat již upravené datum (DATE_ADD). To je asi nejlepší řešení – ale říkám, záleží na podstatě toho sdělení.

2, Napíšeš si vlastní podmínku filtrování (::addFilterDate()->setCondition(function($fluent, $value) {...})).

Editoval Pavel Janda (19. 3. 2016 17:33)

sibka
Člen | 24
+
0
-

@PavelJanda Tak už jsem přišla na to, proč u u některých gridů po aktualizaci na 3.0 nejdou ty filtry. Pokud použiji pro filtrování pole s datem, tak mi to vyhodí chybu „[SQL Server]DATE není známá položka název předdefinované funkce.“ Databázi mám na MS SQL serveru 2014.

Pavel Janda
Člen | 977
+
0
-

@sibka Jaký používáš data source?

sibka
Člen | 24
+
0
-

@PavelJanda Nette\Database

Pavel Janda
Člen | 977
+
0
-

@sibka Používáš jako data source Nette\Databse, nebo Nette\Database\Table\Selection?

Pokud to druhé, můžeš zkusit master? Přidal jsem nový datasource: NetteDatabaseTableMssqlDataSource. Má upravený where pro date/datetime columns + upravený limit().

Pokud to první (tedy s balíčkem ublaboo/datagrid-nette-database-data-source), tak musíš vydržet do večera. :) Píšu si issue.

Editoval Pavel Janda (21. 3. 2016 10:45)

sibka
Člen | 24
+
0
-

@PavelJanda Promiň, nenapsala jsem to úplně, ano používám Nette\Database\Table\Selection.

Vyzkousela jsem master, teď se pro změnu nenačte grid, chyba je u hodnot, které načítám přes cizí klíče při vlastním renderování sloupce. Filtr tedy ani nemohu zkusit.

Pavel Janda
Člen | 977
+
0
-

@sibka Bohužel nemám nyní k dispozici připojení k mssql, tak se to dost blbě testuje.. Můžeš mi pls třeba do gitteru popsat chybu? Díky.

Editoval Pavel Janda (21. 3. 2016 10:53)

sibka
Člen | 24
+
0
-

@PavelJanda S gitterem nemám vůbec žádné zkušenosti :-(

Pavel Janda
Člen | 977
+
0
-

@sibka je to takový velký chat, nic víc. :) Přihlásíš se přes github účet. A nebo pls založ issue na githubu a tam to můžeme probrat, abychom tu nespamovali vlákno. Ale klidně sem, je to asi jedno. O jakou chybu se teď jedná?

Pokusím se vyrobit nějaké mssql připojení.

pitr82
Člen | 121
+
0
-

Pavel Janda napsal(a):

@Attanon Jsou potřeba víceméně 2 jiné queries.

1, $grid->setDataSource($q1): Query, která získá top-level rodiče
2, $grid->setTreeView([$this, 'getChildren'], 'has_children');: $this->getChildren dostane jako parametr $parent_category_id – s tímto parametrem a nějakou další query vrátíš potomky toho žádaného rodiče
3, has_children – druhý parametr metody setTreeView je nějaký „truthy“ sloupec, který určuje, zda mají položky potomky či nikoliv. Buď je to sloupec, nebo to taky může být callback, který bude vracet true/false (dostane tu item jako argument)

Kdybys to chtěl v konkrétním příklady, tak až přijdu domu, pošlu ti ukázku s doctrine.

@PavelJanda l
@Attanon : můžeš mi poslat ukázku s použitím Doctrine, mi to nějak nejde :-) Dík

Attanon
Člen | 25
+
0
-

@pitr82

Já to používám teď momentálně takhle. Ten DataSource je jasný, tam šoupneš QueryBuilder.. já si ho tahám z EntityRepository.

Ohledně toho treeView. Tak první callback mi vrací QueryBuilder. S tím, že tam vyfiltruji všechny na základě toho $parentId.
A ten druhý parametru u treeView tam vracím bool, jestli daná kategorie nějaké potomky má. Vzhledem k tomu, že mám na entity relaci na potomky. Tak to tam jde jednoduše. Ale předpokládám, že to není asi moc efektivní a šlo by to udělat lépe.

$grid->setDataSource($this->entityManager->getRepository(CategoryEntity::class)->createQueryBuilder('er')->where('er.root IS NULL'));

$grid->setTreeView(function ($parentId) use ($presenter) {
	return $presenter->categoryRepository->getEntityRepository()->createQueryBuilder('er')
		->where('er.parent = :parent')->setParameter('parent', $parentId);
}, function (CategoryEntity $categoryEntity) {
	return $categoryEntity->getChildren()->count() != 0;
});

Editoval Attanon (21. 3. 2016 22:52)

sibka
Člen | 24
+
+1
-

@PavelJanda Včera jsem se k tomu už nedostala, tak až dnes jsem založila issue (snad dobře :-)).

sibka
Člen | 24
+
0
-

Jde u „Column Status“ nějak nastavit podmínka, podle které by se přidávaly různé možnosti pro výběr?

Pavel Janda
Člen | 977
+
0
-

@sibka Myslíš aby každý záznam mohl mít jiné možnosti v selectu?