ublaboo/datagrid: mocný, rychlý, rozšiřitelný, hezký, anglicky dokumentovaný datagrid
- Pavel Janda
- Člen | 977
@sevca79 Pokud s tím chceš dělat kouzka, tak si poděď ten
NetteDatabaseDataSource
a tam si můžeš dle libosti upravovat
query pro stránkování, filtry a pod..
- chap
- Člen | 81
Pavel Janda napsal(a):
@chap Myslíš již od někoho napsané? Já můžu poskytnout CS překlady. EN je by default. DE by se dala udělat. Super nápad, přidám jich pár na web.
Ahoj, nebyla by prosím německá mutace? Nechce se mi to prohánět google translatorem – a na víc se nezmůžu :) Jinak nahrát i jiné jazyky by bylo moc fajn.
- Pavel Janda
- Člen | 977
@Klobasa Použij buď custom anonymní funkci pro vykreslení sloupce (http://ublaboo.org/datagrid/column#…) nebo použij šablonu a definici bloku pro sloupec (http://ublaboo.org/…rid/template#…). :)
- Pavel Janda
- Člen | 977
@Chipso Co konkrétně bys potřeboval vědět? datagrid bere jako datasource doctrine query builder nebo doctrine collection.
- kolsi
- Člen | 131
Ahoj, zalíbil se mi tento datagrid a uvažuji, že bych na něj přešel z Grida. Ale nejdřív bych se rád zeptal, zda je v něm nějak řešeno použití vlastních počítaných sloupců (nebo jak to nazvat). Myslím situaci, kdy mám všechna data z databáze (NDB), ale jeden sloupec z DB není, protože se jeho data počítají/závisí na něčem, co v DB není uloženo.
V Gridu sice existuje možnost použít setCustomRender nad sloupcem, ale to ovlivní pouze to, co se vykreslí. Takže na první pohled vše vypadá dobře, ale nelze takový sloupec seřadit, filtrovat, vůbec nic.
Další možnost je, že chci v datagridu jeden sloupec, aby byl sloučením více sloupců z DB (např. sloupec „user“ jako CONCAT(first_name, surname)). Řešením by bylo použití agregačních funkcí a aliasů:
Pavel Janda napsal(a):
@Ivorius Tohle mi přijde naprosto korektní:
$grid->setDataSource($selection->select('orders.*,user.login AS login'));
Jenže ani toto nebude fungovat dobře. Sice se opět vypíše správný výsledek, ale stále nefunguje řazení ani filtrování (protože MySQL neumí WHERE nad aliasem).
- Pavel Janda
- Člen | 977
@kolsi Ano, je potřeba sloupec nějak vypsat, pak řadit a pak filtrovat.
- Custom sloupec vypíšeš buď tím vlastním callbackem nebo blokem definovaným v šabloně.
- Ano, bude potřeba callback pro vlastní podmínku filtrování
- Vezmeš ten alias z query a dáš ho metodě
$customColumn->setSortable($alias);
Kdyžtak se pak ptej.
@MW Není. Akorát je to jednodušší. Je to jako composer pro JS.
Doporučuju vyzkoušet.
Všechny assety jsou na boweru, odtud se tahají z githubu (jako na composeru),
takže si stačí otevřít bower.json a najít na boweru (a posléze githubu)
stejnojmenné balíčky.
- MW
- Člen | 626
@PavelJanda Musím uznat, že přicházím Boweru na chuť.. diky !
Zkoumám grid a vypadá vážně skvěle!
Jak se prosím řeší join tabulky přes NDB? Typicky název produktu přes ID v jiné tabulce.. aby se podle toho dalo i filtrovat. V dokumentaci jsem zatím nenalezl..
Určitě budu i potřebovat např. filtrovat selectboxem kde bude ID ⇒ nazev.
Jak toto skloubit na tomto gridu?
Moc diky!
- kolsi
- Člen | 131
Pavel Janda napsal(a):
@kolsi Ano, je potřeba sloupec nějak vypsat, pak řadit a pak filtrovat.
- Custom sloupec vypíšeš buď tím vlastním callbackem nebo blokem definovaným v šabloně.
- Ano, bude potřeba callback pro vlastní podmínku filtrování
- Vezmeš ten alias z query a dáš ho metodě
$customColumn->setSortable($alias);
Kdyžtak se pak ptej.
Děkuji za odpověď. Zkouším si s tím hrát a hned se tedy zeptám.
Toto funguje parádně včetně řazení a filtrování:
$grid->addColumnText('activity', 'Activity', "activity.name")->setSortable()->setFilterText();
Ale teď potřebuji dva případy a ty nemohu rozběhat správně. Uvádím zjednodušený kód.
- Sloupec, který spojí dva sloupce dohromady (např. datum+čas). V tabulce se vypíše dobře, řazení funguje dobře, ale filtrování vyhodí, že nezná sloupec „date_start“.
$grid->addColumnDateTime("date_start", "Start")->setFormat("j.n.Y G:i")->setSortable()->setFilterDateRange();
...
$grid->setDataSource($this->context->timesheetRepository->findAll()->select("*, TIMESTAMP(date, time_start) AS date_start"));
Chápu dobře toto řešení?
->setFilterDateRange()->setCondition(function($model, $value) {
$model->where("TIMESTAMP(date, time_start) BETWEEN ? AND ?", $value["from"], $value["to"]);
});
A tedy nutnost specifikovat tuto podmínku vždy, když budu chtít použít podobný sloupec? Neexistuje nějaké univerzálnější řešení?
- Sloupec s hodnotou nezávislou na jiných datech z DB. Např. podle uživatelského nastavení se vypisují jména v různém formátu. Výpis ok, při řazení/hledání chyba, že nezná sloupec „user“.
$grid->addColumnText('user', 'User')->setRenderer(function($item) { return $this->getFullUserName($item->user_id); });
- BuGeR
- Člen | 45
Ahoj, setkal jsem se se zajimavým problémem.
Když přidám do gridu vlastní komponentu, viz:
$grid->addComponent(new Multiplier(function($userId) {
$user = $this->userRepository->find($userId);
$control = $this->profileModalFactory->create($user);
return $control;
}), 'profileModal');
.. a potom si v detailu tuto komponentu vykresluju, viz:
{extends $original_template}
{block detail}
<a n:href="profileModal-$item->id-showModal!" class="ajax showModal">upravit profil</a><br />
{control profileModal-$item->id}
...
Tak se mi stává, že pokud ta komponenta obsahuje formulář a je celá načítána ajaxem, tak při prvním načtení, se ten formulář sice zobrazí, ale není obalen značkama <form>. Pokud k tomu ještě dám odkaz na handleRefresh, který mi tu komponentu překreslí, a přidám tam něco jako <a n:href=„refresh!“ class=„ajax“>refresh</a>, a prokliknu to, tak se to tím formulářem najednou obalí, a jde odeslat.
Jednoduše řečeno, pokud přidám do gridu vlastní komponentu s formulářem, a načtu jí ajaxem, tak při prvním načtení není formulář obalen do <formu>, a nejde odeslat.
Nevíte prosím někdo jak toto vyřešit?
Díky.
- Pavel Janda
- Člen | 977
@BuGeR Celý datagrid je jeden velký formulář. Nemůžeš dát dovnitř další formulář. není to validní HTML.
Momentálně by to chtělo poslat nějaký geniální pull request do nette, aby bylo podporováno HTML5. Mohl bych pak rozsekat části datagridu do více formulářů a přitom používat formulářové prvky napříč celou stránkou.
Jediná možnost v nette je momentálně ošéfovat všechno JavaScriptem.
- Rankin
- Člen | 5
Ahoj, chtěl bych Vás poprosit o radu při použití gridu s MSSQL. Používám NetteDatabaseDataSource, předám Context s odpovádající Connection, jednoduché SQL a ihned to havaruje na špatně vygenerované (MySQL) syntaxi s LIMIT na konci. Má někdo obdobnou zkušenost? Díky za jakékoli rady.
- CZechBoY
- Člen | 3608
@Rankin Myslím, že je třeba použít speciální MsSql
datasource. Pokud vytváříš přímo IDataSource tak použij tuto třídu.
Nebo můžeš vykopírovat kod, který to dělá v Datagridu
https://github.com/…ataModel.php#…
Editoval CZechBoY (26. 1. 2017 9:31)
- Rankin
- Člen | 5
@CZechBoY Tak problém je zřejmě ještě hlouběji. Moc díky za radu, ale MSSQL datasource chce Selection… Context mi ale při this->ndb->table(‚tabulka‘) řve, že tabulka neexistuje, přičemž při testu normálního this->ndb->query(‚SELECT * FROM tabulka‘) je vše OK. Nevíš kde by tady mohl být zakopaný pes?
- Pavel Janda
- Člen | 977
@Rankin Pro NDB je v dalším balíčku (jak zmínil @CZechBoY ) NetteDatabaseDataSrouce. Nejlepší bude, když si tuhle třídu podědíš a upravíš s iLIMIT a OFFSET tak, aby se volala adekvátní query se stránkováním pro tvůj server (sqlsrv, mssql, apod + různé roky vydání sql serveru mají různé způsoby, jak „přechcat“ chbející LIMIT + OFFSET). Pokud bys používat NDBT, tak již v balíčku ublaboo/datagrid takový přechcávací mechanismus je.
- Rankin
- Člen | 5
@PavelJanda Pavle prosím ještě o radu, určitě není problém to přepsat, nicméně dělám něco špatně? NetteDatabaseDataSrouce právě už používám. Přesto to vyhodí tu chybu…
$this->ndb = $this->context->getService('mssql');
$sql = 'SELECT * FROM dba.zbozi';
$datasource = new NetteDatabaseDataSource($this->ndb, $sql);
$grid->setDataSource($datasource);
- Pavel Janda
- Člen | 977
@Rankin Jakou chybu to vyhodí?
Sice to asi nesouvisí s chybou, ale best-practice je netahat služby z context, ale předávat si je parametrem. Když pogoogliš na nette foru, myslím, že najdeš dobré příklady.
- Pavel Janda
- Člen | 977
@Rankin Rozumím. Četl jsi můj příspěvek výše? Psal jsem, že
by bylo asi vhodné podědit právě ten NetteDatabaseDataSource
a
upravit si metodu na „vyrábění“ pagiantoru tak, aby query korespondovala
v tebou využívanou verzí msssql.
- MW
- Člen | 626
Tak jsem začal a malinko jsem narazil a prosím o pomoc:
1/ prosím o radu, jak se tedy řeší join tabulky přes NDBT? Typicky název produktu přes ID v jiné tabulce.. aby se podle toho dalo i filtrovat. V dokumentaci jsem zatím nenalezl..
2/ jak je to s validací u formuláře pro inlineAdd/Edit? Když dám ->setRequired() , tak mě to neodešle např. změnu počtu položek na stránku.
3/ grid jsem rozchodil dle dokumentace, i když hlásí chyby v konzoli v datagrid.js, ale to nebude příčinou problemu, že mě po inline přidávání záznamu nehodí flashmessage a ani nerefreshne grid:
Edit: InlineEdit funguje OK, ulozi, invaliduje radku, ale neukáže také Flashmessage :/
Kde dělám chybu ? (Udělal jsem si to jako komponentu) :
Presenter:
public function createComponentUserGrid() {
return $this->usersGridControlFactory->create();
}
Továrna na grid:
<?php
namespace App\Grids\UsersGrid;
interface UsersGridControlFactory
{
/**
* @return UsersGridControl
*/
public function create();
}
Conrol:
<?php
namespace App\Grids\UsersGrid;
use App\Model\UserModel;
use Nette\Application\UI\Control;
use App\Grids\UblabooGridFactory;
class UsersGridControl extends Control {
/** @var UblabooGridFactory */
private $ublabooGridFactory;
/** @var UserModel */
private $userModel;
public function __construct(UblabooGridFactory $ublabooGridFactory, UserModel $userModel) {
$this->ublabooGridFactory = $ublabooGridFactory;
$this->userModel = $userModel;
}
/**
* @return Grid
*/
protected function createComponentGrid() {
$grid = $this->ublabooGridFactory->create();
$this->decorateGrid($grid);
return $grid;
}
public function decorateGrid($grid) {
$grid->setDataSource($this->userModel->getUsersTable());
$grid->addColumnText('id', 'Id');
$grid->addColumnText('username', 'Uživ. jméno');
$grid->addColumnText('name', 'Jméno');
$grid->addColumnText('password', 'heslo');
$grid->addColumnText('role', 'Oprávnění');
$grid->addInlineAdd()
->setPositionTop()
->onControlAdd[] = function($container) {
$container->addText('id', 'ID')->setAttribute('readonly');
$container->addText('username', 'Uživ. jméno');
$container->addText('name', 'Jméno');
$container->addText('password', 'Heslo');
$container->addText('role', 'Oprávnění');
};
$p = $this;
$grid->getInlineAdd()->onSubmit[] = function($values) use ($p) {
$p->userModel->saveData($values);
$p->flashMessage("Data uložena", 'success');
$p->redrawControl('flashes');
};
return $grid;
}
public function render() {
$this->template->render(__DIR__ . '/templates/default.latte');
}
}
Tovarna na Ublaboo:
<?php
namespace App\Grids;
use Ublaboo\DataGrid\DataGrid;
final class UblabooGridFactory
{
/**
* @return Grid
*/
public function create()
{
$grid = new DataGrid();
DataGrid::$icon_prefix = 'glyphicon glyphicon-';
return $grid;
}
}
a v render template jen:
{snippet}
{control grid}
{/snippet}
Data získávám přes NDBT:
public function getUsersTable() {
return $this->database->table('users');
}
Editoval MW (26. 1. 2017 18:44)
- MW
- Člen | 626
Tak část jsem vyřešil – i když nevím zdali optimálně: (problém je, že to mám přes továrny):
$grid->getInlineAdd()->onSubmit[] = function($values) use ($p) {
$p->userModel->saveData($values);
$p->flashMessage("Data uložena", 'success');
$p->redrawControl('grid');
$p->redrawControl('flashes');
};
a v šabloně komponenty, kde jsem musel přidat ty snippety (v dokumentaci to je bez nich):
{snippet flashes}
<div n:foreach="$flashes as $flash" n:class="flash, $flash->type">{$flash->message}</div>
{/snippet flashes}
{snippet grid}
{control grid}
{/snippet}
Jinak to nešlo, nebo jsem na to nepřišel :) Pokud to jde poslat nějak do presenteru nebo do samotné komponenty, možná by to bylo lepši !?
Prosím tedy ještě jen o radu ohledně validace u inline a JOINu viz výše :)
Diky!
Editoval MW (26. 1. 2017 22:47)
- marek-m
- Člen | 66
Mam taky mensi problem, ako viem na konkretny td nastavit nejaky style. Napr. mam tabulku so stlpcom color, tu chcem pre konkretny row nastavit background-color pomocou udaju z dbf color. Casto to napr. pouzivam pri stavoch objednavok, kde to aj pomocou color sprehladnujem adminom. Nechcem vsak background-color nad celim row. addColumnCallback mi nepomahal.
Dakujem
ked som to napisal, tak som nasiel riesenie :-)
$grid->addColumnCallback('color', function($column, $item) {
$td = $column->getElementPrototype('td');
$td->style[] = 'background-color: ' . $item->color . ';';
});
Editoval marek-m (29. 1. 2017 18:24)
- jan-stanek
- Člen | 9
Ahoj, chtěl bych se zeptat, jestli je v plánu přidání skupinové akce s MultiSelect polem.
- kolsi
- Člen | 131
kolsi napsal(a):
Pavel Janda napsal(a):
@kolsi Ano, je potřeba sloupec nějak vypsat, pak řadit a pak filtrovat.
- Custom sloupec vypíšeš buď tím vlastním callbackem nebo blokem definovaným v šabloně.
- Ano, bude potřeba callback pro vlastní podmínku filtrování
- Vezmeš ten alias z query a dáš ho metodě
$customColumn->setSortable($alias);
Kdyžtak se pak ptej.
Děkuji za odpověď. Zkouším si s tím hrát a hned se tedy zeptám.
Toto funguje parádně včetně řazení a filtrování:
$grid->addColumnText('activity', 'Activity', "activity.name")->setSortable()->setFilterText();
Ale teď potřebuji dva případy a ty nemohu rozběhat správně. Uvádím zjednodušený kód.
- Sloupec, který spojí dva sloupce dohromady (např. datum+čas). V tabulce se vypíše dobře, řazení funguje dobře, ale filtrování vyhodí, že nezná sloupec „date_start“.
$grid->addColumnDateTime("date_start", "Start")->setFormat("j.n.Y G:i")->setSortable()->setFilterDateRange(); ... $grid->setDataSource($this->context->timesheetRepository->findAll()->select("*, TIMESTAMP(date, time_start) AS date_start"));
Chápu dobře toto řešení?
->setFilterDateRange()->setCondition(function($model, $value) { $model->where("TIMESTAMP(date, time_start) BETWEEN ? AND ?", $value["from"], $value["to"]); });
A tedy nutnost specifikovat tuto podmínku vždy, když budu chtít použít podobný sloupec? Neexistuje nějaké univerzálnější řešení?
- Sloupec s hodnotou nezávislou na jiných datech z DB. Např. podle uživatelského nastavení se vypisují jména v různém formátu. Výpis ok, při řazení/hledání chyba, že nezná sloupec „user“.
$grid->addColumnText('user', 'User')->setRenderer(function($item) { return $this->getFullUserName($item->user_id); });
Lze tedy tohoto nějak vůbec dosáhnout?
…
Dále bych měl ještě pár dotazů:
- lze horní lištu jednoduše přesunout do zápatí? Přijde mi plýtvání místem, že se zobrazí horní lišta pouze s tlačítkem pro skrývání sloupců, když toto tlačítko by se krásně vešlo do zápatí (např. vedle selectu pro počet položek/stránku). A nakonec i select s operacemi by se do zápatí vešel.
- presentery máme v utf-8, šablony jsou v utf-8, data v databázi jsou v utf-8, přesto musím v parametru fce addExportCsv explicitně uvést windows-1250, jinak je exportované CSV při otevření v Excelu nečitelné.
- rumcais1
- Člen | 80
Ahoj narazil jsem na tohle s group-action pokud vyfiltruji neexistující záznam a pak tento filtr smažu chechboxy se dosajadi ajaxove do prvniho sloupce a celé se to posune. viz demo http://ublaboo.org/datagrid/?…
- Pavel Janda
- Člen | 977
@kolsi K tomu exportu: excel má s utf-8 problém, proto se musí exportovat do windows-1250.
- cujan
- Člen | 410
dobry den, ako namapovat nette/database zdroj, nejako sa mi to nedari…
public function createComponentRecordGrid1()
{
$grid = new \Ublaboo\DataGrid\DataGrid();
$grid->setDataSource($this->recordManager->getRecords());
$grid->addColumnText('idForwarderIn', 'Zaslal kto');
return $grid;
}
SQLSTATE[42S22]: Column not found: 1054 Unknown column ‚id‘ in ‚order clause‘
Editoval cujan (30. 1. 2017 12:38)
- Pavel Janda
- Člen | 977
@cujan Jaký používáš primární klíč? Pokud ne ID, tak ho můžeš nastavit metodou DataGrid::setPrimaryKey
@rumcais1 Hmm, jdu se na to podívat, díky za report.
@marek-m Jakou používáš verzi?
- Pavel Janda
- Člen | 977
@kolsi Můžeš si libovolně překládat šablonu datagridu – podědíš, přeskládáš. :)
- cujan
- Člen | 410
Pavel Janda napsal(a):
@cujan Jaký používáš primární klíč? Pokud ne ID, tak ho můžeš nastavit metodou DataGrid::setPrimaryKey
@rumcais1 Hmm, jdu se na to podívat, díky za report.
@marek-m Jakou používáš verzi?
oki funguje, ale nefunguje odkazovanie na tabulku cez cudzi kluc
public function createComponentRecordGrid1()
{
$grid = new \Ublaboo\DataGrid\DataGrid();
$grid->setPrimaryKey('idRecord');
$grid->setDataSource($this->recordManager->getRecords());
$grid->addColumnText('idForwarderIn', 'Zaslal kto',':forwarder.name');
return $grid;
}
No reference found for $record->related(forwarder).
zeby bol problem tiez s cudzim klucom?
- cujan
- Člen | 410
cujan napsal(a):
Pavel Janda napsal(a):
@cujan Jaký používáš primární klíč? Pokud ne ID, tak ho můžeš nastavit metodou DataGrid::setPrimaryKey
@rumcais1 Hmm, jdu se na to podívat, díky za report.
@marek-m Jakou používáš verzi?
oki funguje, ale nefunguje odkazovanie na tabulku cez cudzi kluc
public function createComponentRecordGrid1() { $grid = new \Ublaboo\DataGrid\DataGrid(); $grid->setPrimaryKey('idRecord'); $grid->setDataSource($this->recordManager->getRecords()); $grid->addColumnText('idForwarderIn', 'Zaslal kto',':forwarder.name'); return $grid; }
No reference found for $record->related(forwarder).
zeby bol problem tiez s cudzim klucom?
@PavelJanda
teoreticky to funguje bez tej dvojbodky, ale mam kus problem so zobraznim, mam
v table record dva stlpce
idForwarderIn
idForwarderOut
odkazuju cez cudzi kluc na tab forwarder,
ale ked da vypisat data tak jedna aj druha polozka ukazuju tu istu hodnotu…aj
ked su v db rozne…preco?
public function createComponentRecordGrid1()
{
$grid = new DataGrid();
$grid->setDataSource($this->recordManager->getRecords());
$grid->addColumnDateTime('shippingDate', 'Dátum prijatia záznamu');
$grid->addColumnText('idForwarderIn', 'Zaslal kto','forwarder.name');
$grid->addColumnDateTime('dateOfTransmission', 'Zaslal - dátum');
$grid->addColumnText('fileNumber', 'číslo spisu odosielateľa');
$grid->addColumnText('subject', 'Vec');
$grid->addColumnText('processor.surname', 'Spracovateľ');
$grid->addColumnDateTime('dateOfSubmission', 'Dátum odovzdania');
$grid->addColumnDateTime('implementationDate', 'Dátum vybavenia');
$grid->addColumnText('idForwarderOut', 'Vybavené dresát','forwarder.name');
$grid->addColumnText('shippingType.name', 'Vybavené - ako');
$grid->addColumnText('registrationMark.name', 'RZ');
$grid->addColumnDateTime('disposal', 'Záznam o vyradení');
return $grid;
}
Editoval cujan (30. 1. 2017 21:14)
- jan-stanek
- Člen | 9
@PavelJanda Díky moc. Snad nedělám něco špatně, ale multiselect se mi vůbec nevykresluje.
- jan-stanek
- Člen | 9
$grid = new DataGrid($this, $name);
$grid->setTranslator($this->translator);
$grid->setDataSource($this->userRepository->createQueryBuilder('user'));
$grid->setColumnsHideable();
$grid->addGroupSelectAction('Send', [
'john' => 'John',
'joe' => 'Joe',
'frank' => 'Frank',
])->onSelect[] = [$this, 'groupSend'];
$grid->addGroupMultiSelectAction('SendMulti', [
'john' => 'John',
'joe' => 'Joe',
'frank' => 'Frank',
])->onSelect[] = [$this, 'groupSend'];
Pak už se přidávají jen sloupce. Klasický select funguje správně.