ublaboo/datagrid: mocný, rychlý, rozšiřitelný, hezký, anglicky dokumentovaný datagrid
- Pavel Janda
- Člen | 977
@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
@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
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
@Bedna Hm, confirmation dialog je navázaný na ajax requesty. Do
příští verze to rozšířím a pravděpodobně i prefixnu:
data-confirm
⇒ data-datagrid-confirm
.
- majky358
- Člen | 37
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
@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
@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
@majky358 Takže ok? :)
Ano, první query nemůže fungovat, když nemá joinutou potřebnou tabulku..
- Arty
- Člen | 3
Ř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
@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
@Arty Asi nejrychlejší řešení z mé strany je přidat Property Access (u věcí ORM) do ArrayDataSource. Dám to do issue.
- Pavel Janda
- Člen | 977
@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
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
@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“
- Pavel Janda
- Člen | 977
@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
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.
- Pavel Janda
- Člen | 977
@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
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
@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
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 jensetIcon()
, 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)
- Pavel Janda
- Člen | 977
@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
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
@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
@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
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
- Pavel Janda
- Člen | 977
@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
@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
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
- Pavel Janda
- Člen | 977
@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
@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)
- Pavel Janda
- Člen | 977
@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)
- Pavel Janda
- Člen | 977
@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)
- Pavel Janda
- Člen | 977
@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
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 metodysetTreeView
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
@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)