Grido – DataGrid pro Nette
- o5
- Člen | 416
Release 2.1
Někdo si už možná všiml, včera (po téměř roce) vyšla verze 2.1.0. Obsahuje 84 commitů a jedná se hlavně o fixy a pár drobných vylepšení. Nejdůležitější je, že by tato verze neměla nic rozbít a tedy doporučuji všem si změnit verzi v composer.json na 2.1, jelikož @dev verze už nyní obsahuje (pro někoho možné) BC.
Hlavní změna v 2.1:
- Asi nejdůležitější se odehrálo v Grido\Components\Export. Tato
komponenta by nyní měla fungovat správně i s velkým počtem dat a lze
nyní prostřednictvím
$export->setFetchLimit()
ovlivnit, kolik řádků najednou se má vytáhnout z DB (výchozí limit je 100000). Jako bonus přibyly možnosti, kterými lze ovlivnit výsledný export. Pomocí$export->setHeader()
lze nastavit hlavičku CSV (labely sloupců) a pomocí callbacku$export->setCustomData()
lze exportovat i sloupec, který není definován. V dokumentaci to zatím není, prozatím se bude muset stačit podívat do testů
Příští verze bude 3.0:
Další verze bude 3.0 a bude vyžadovat PHP 5.5 (PHP 5.5 je venku skoro 2,5roku a PHP 5.4 je měsíc mrtvé, viz Supported Versions). Dále bude Grido pravděpodbně vyžadovat jednotlivé nette balíky >=2.3. Změny jsou plánované také v client-side. Novinky jsou v plánu také a osobně je považuji za killer features (označil jsem je WIP jelikož je již použivám v pár projektech, takže je to „jen“ otázka přehodit do master větve). Sepsané je to v Milestones.
Donate
Grido je celkem rozsáhlá komponenta (více jak 10k řádků) s aktuálně 95% pokrytím unit testy! Když se podívám na zdejší thread (asi největší na Nette fórum) a počet instalací přes Composer (26k) tak hádám, že Grido je celkem používané a dobře slouží. Taky je tu s námi skoro 3 roky! Nyní nastal čas Grido ocenit. Úvodní thread jsem aktualizoval a přidal Donors list.
Editoval o5 (3. 11. 2015 16:51)
- VK
- Člen | 10
@o5 Díky za další vývoj a určitě rád z další faktury přispěji, ale mám prosbu…proč je verze 2.1.1 omezena na Nette < 2.3.0? Je tam nějaký problém? Používám nejnovější Nette 2.3.7, s grido 2.0.8 problém nebyl a novější verze to najednou limituje. Šlo by s tím něco udělat? Případně mi popiš s čím tam je problém a zkusil bych se na to taky podívat (chápu, že nemusíš mít na to čas) a upravil tak, aby toto omezení mohlo být odstraněno. Řešením by bylo samozřejmě použít dev větev, ale to už jsem si dávno a po dost špatných zkušenostech s jinými balíčky zakázal. Díky za info.
- o5
- Člen | 416
@VK: Locknutí Nette ve verzi 2.1.1 bylo trochu unáhlené. Na githubu někdo hlásil problémy s novým na Nette a touto branche 2.1, ale až teď mi došlo, že ten člověk zřejmě nepoužil composer, protože by jinak takový problém mít neměl, jelikož díky tomuhle by se mu měl nainstalovat i balík nette/deprecated, který by to měl řešit.
Pokud se ti nechce použít @dev větev, lockni si na
"o5/grido": "2.1.0"
.
- Oli
- Člen | 1215
@o5 je nějaká možnost jak obarvit celý řádek gridu? Místo
<tr class="grid-row-2">
bych potřeboval
<tr class="grid-row-2 warning">
u některých sloupců. Ta
třída se tam přidává napevno, ale předpokládám, že by šlo udělat
nějaký dynamický nastavení třídy. Pokud už něco takovýho není
v plánu pro verzi 3?
- Mirka
- Člen | 5
Ahoj,
prošla jsem snad vše, co se o Grido psalo. Našla jsem několik řešení,
ale na aktuální verzi nefungují.
Čas v DB mám unix a nedaří se mi správně nastavit FilterDate.
$grid->addColumnDate('created_at', 'Created', \Grido\Components\Columns\Date::FORMAT_DATETIME)
->setFilterDate()
->setWhere(function($value, $selection) {
$date = $this->paymentManager->unixDB($value);
$selection->where('created_at LIKE ', $date);
});
Tohle řešení jsem našla v tomto vláknu, ale neděje se nic, ani
žádná chyba, filtr je prázdný, value se nevyplní…
Můžete mě někdo prosím nakopnout?
Děkuji :)
- CZechBoY
- Člen | 3608
Neřešil někdo case insensitive filtrování?
V MySQL klasicky nastavím _ci sloupec, ale v oraclu mi to nějak nejede,
nebo nevim.
Jde nastavit vyhledávání tak, aby pro text se sloupec i hodnota převedla na uppercase (např)?
SELECT * FROM tabulka WHERE upper(sloupec1) LIKE upper('%hodnota%');
Editoval CZechBoY (24. 11. 2015 14:33)
- o5
- Člen | 416
@CZechBoY: je potřeba změnit výchozí generování condition pro text filter.
Tam kde používám PostgeSQL to řeším takovým drobným hackem, který
spočívá v použití vlastního text filtru. Překryju si
__getCondition()
v Grido\Components\Filter\Text.
<?php
namespace App\Controls\Grido\Filters;
/**
* Text input filter.
*
* @property int $suggestionLimit
* @property-write callback $suggestionCallback
*/
class Text extends \Grido\Components\Filters\Text
{
const DEFAULT_CONDITION = 'LIKE ?';
/** @var string */
protected $condition = self::DEFAULT_CONDITION;
public function __getCondition($value)
{
if ($value === '' || $value === NULL) {
return FALSE; //skip
}
$condition = $this->getCondition();
if (is_string($condition) && $condition === self::DEFAULT_CONDITION) {
$columns = $this->getColumn();
foreach ($columns as &$column) {
$column = Condition::isOperator($column)
? $column
: "LOWER(CAST($column AS TEXT))";
}
$condition = Condition::setup($columns, 'LIKE LOWER(?)', $this->formatValue($value), FALSE);
} else {
$condition = parent::__getCondition($value);
}
return $condition;
}
}
- Je samozřejmě vhodné si rovněž překrýt metodu
$grid->addFilterText()
(třeba ve společném předku všech gridů).
- vymak
- Člen | 92
Ahoj,
měl bych na Vás jeden dotaz. Je nějak možné po provedení inline editace,
aby došlo k revalidaci celého řádku, na kterém byla
úprava provedena?
$that = $this;
$grid->addColumnText('name', _('Stav'))
->setEditable()
->setEditableCallback(function ($id, $newValue) use ($that) {
$this->dictaphoneRepository->update($id, array('state_id' => $newValue));
$that->redrawControl(); // toto nějak nefunguje
return TRUE;
})
->setEditableControl($this->getSelect())
->setEditableValueCallback(function ($row) {
return $row->state_id;
})
->setReplacement($this->dictaphoneRepository->getStateArr())
->setSortable()
->setFilterText()
->setSuggestion();
Těším se na Vaše odpovědi :).
- VK
- Člen | 10
Narazil jsem na problém při použití vnořeného objektu (používám Doctrine data source). V Gridu na toto existuje setter setColumn(). Jenže v případě, že editor (což je referenční vazba v db) je nulový, tak použitý Symfony „property accessor“ vyhazuje exception, že udávaná cesta neexistuje (což je pravda, protože editor vrací null a ne objekt).
Otázka tedy, zda mi něco uniká a dá se to vyřešit v Gridu ještě nějakým nastavením a pokud ne, tak bych byl pro řádek #598 nahradit tímto:
if (!$this->getPropertyAccessor()->isReadable($object, $name)) {
return null;
} else {
return $this->getPropertyAccessor()->getValue($object, $name);
}
Pokud souhlas, tak můžu poslat PR.
- Oli
- Člen | 1215
@cujan sekl jsem se ve verzích (psal jsm to z mobilu a neověřoval
jsem to). Je to 2.1.0
. 2.1.1. mám pocit, že právě nejde kvůli
nahlášení issue a rychlému hotfixu, kterej ale nebyl problémem.
@dev
verze každopádně jde určitě.
A nette si nastav na 2.3.* Ono to je potom i víc vidět jakou používáš verzi.
- helvete
- Člen | 16
libik napsal(a):
Grido mi zobrazovalo 11 zaznamu (strankovani po 10). Na 2. strance jsem smazal ten 11. a koncim v ladence s notice „Page is out of range.“
<?php 439: trigger_error(„Page is out of range.“, E_USER_NOTICE);?>
Nastaveni grida mam:
<?php $grid = new \Grido\Grid($this, $name); $grid->setRememberState(true); $grid->setModel($this->items->findAll()); $grid->setPerPageList(array(10, 20, 50)); $grid->setDefaultPerPage(10); $grid->translator->lang = 'cs'; ... ?>
V cem muze byt problem?
Ahoj podobnou vec jsem resil dnes. Snadno vyresis pretizenim metody \Grido\Grid::getData(). V casti kde je
<?php
if ($applyPaging && $data && !in_array($this->page, range(1, $this->getPaginator()->pageCount))) {
$this->__triggerUserNotice("Page is out of range.");
$this->page = 1;
}
?>
staci upravit na:
<?php
if ($applyPaging && $data && !in_array(
$this->page, range(1, $this->getPaginator()->pageCount))
) {
$this->page = $this->getPaginator()->pageCount;
}
?>
Nevyhazuje se potom notice a user je presmerovan na posledni stranku, ktera je k dispozici.
- Oli
- Člen | 1215
@CZechBoY co ti na tom nejde? Já to používám jedině tak. Je to uplně stejný, jako by jsi to měl v presenteru.
class UserComponent extends Control
{
private $grido;
protected function createComponentGrid($name)
{
$this->grido = new \Grido\Grid($name);
// ...
return $this->grido;
}
}
- CZechBoY
- Člen | 3608
@Oli hm, tak to je divný. Dává mi to do url parametry bez prefixu
jména komponenty. Prostě jen filter[xxx]=aaa
.
V render metodě dáváš normálně
$this['grido']->render();
nebo jak?
Já tu komponentu dělám trošičku jinak a nevim jestli v tom nebude problém.
protected function createComponentGrido ()
{
$grido = $this->gridoFactory->create($this->selection);
.
.
.
return $grido;
}
GridoFactory je potom továrnička vytvářející Grido
class Grido extends \Grido\Grid
{
public function __construct (Selection $selection = null, IContainer $parent = null, $name = null)
{
parent::__construct($parent, $name);
if ($selection) {
$this->setModel($selection);
}
}
}
Editoval CZechBoY (15. 12. 2015 19:29)
- Oli
- Člen | 1215
Historicky si na to vytvářím šablonu (dřív jsem tam měl další věci,
teď už jen tu komponentu), ale nemělo by to na to mít vliv. Filtr mě to
generuje dobře: ?grid-grid-filter%5Bid%5D=6496
Vytvářím to teda
ale takhle $this->grid = new \Grido\Grid($this, $name);
. Ten
příspěvek předtím jsem psal z hlavy, tak nevím jestli to na to nemůže
mít vliv (jeslti to vytvářiš s jedním parametrem $name).
- chap
- Člen | 81
Mart78 napsal(a):
Mohl by mi někdo poradit jak název sloupce s akcemi? Sloupce to berou z parametru, ale odkud sloupec s akcemi? Hledal jsem v dokumentaci i v API.
Asi chceš přejmenovat sloupec „akce“ – to lze pomocí vlastního
translatoru:
$grid->setTranslator($tvujTranslator);
Překlady tu:
https://github.com/…tions/cs.php
- drick
- Člen | 61
Zaujimalo by ma, ci je nejak mozne spojit do modelu viacero tabuliek Nette Database.
Teda, chcel by som nieco taketo:
$grid->model = $this->database->table(‚users‘);
$grid->model = $this->database->table(‚settings_for_users‘);
Zial, ked to takto zapisem, tak $grid->model nerozozna prvu tabulku. Je mozne tam nejak hodit 2 tabulky naraz?
- Maxell92
- Člen | 38
Ahoj, měl bych dotazy k inline editaci:
- Je možné uložit hodnotu hned po výběru data z DatePickeru? Takhle se musí vybrat hodnota, znovu kliknout do inputu a dát enter.
- Je možné po uložení udělat refresh řádku? Některé další sloupce mají podmínky, které může editace ovlivnit a měla by se zobrazit jiná hodnota.
- Občas se mi dataPicker chová divně – Při každém focusu na input se nastaví na dnešní datum. Což je v kombinaci s bodem 1 dost nepoužitelné :) Dělala mi to i editace v online příkladu, ale teď jsem to zkoušel znovu a tam to funguje. Nějaký tip, čím by to mohlo být?
- Maxell92
- Člen | 38
Odpovím si sám, alespoň na část :)
1 + 3 – zavedl jsem si vlastní datePicker:
<script>
var initDatePicker = function(element, sendAfterSelectDate) {
$(element).datetimepicker({
onSelectDate: function() {
if (element && sendAfterSelectDate) {
$(element).trigger(jQuery.Event("keypress.grido", {keyCode: 13}))
}
}
});
};
window.Grido.Grid.prototype.onInit.push(function(Grido)
{
Grido.$element.on('focus', 'input.date-picker', function() {
initDatePicker($(this), true);
});
});
</script>
Obdobně jsem udělal uložení po kliku mimo input:
<script>
window.Grido.Grid.prototype.onInit.push(function(Grido)
{
Grido.$element.on('focusout', 'input:not(.date-picker)', function() {
$(this).trigger(jQuery.Event("keypress.grido", {keyCode: 13}));
});
});
</script>
- Oli
- Člen | 1215
@Maxell92 odpověď na tvou 2. otázku je myslím
$grid->reload()
. A co se týče 1 a 2, není to co tu
popisuješ náhodou řešení tohohle issue?
- ch4rli3
- Člen | 6
Dobrý den,
procházel jsem tuto diskuzi a nemůžu najít řešení na následující
problém – nebo mám já něco špatně:
mám 2 tabulky v databázi:
jedna je package_kind se strukturou: id, material
druhá je products se strukturou: id, productName, package_kind_id
V gridu se snažím vypsat produkty tak, aby se mi zobrazil přímo material z tabulky package_kind
Zkoušel jsem to takto:
$grid->addColumnText('package_kind_id', 'Druh obalu')
->setColumn(function($item){
return $item->package_kind->material;
});
nicméně mi to nefunguje a pořád mi to hází „Cannot read an undeclared column ‚package_kind‘“
Napadá někoho prosím co dělám špatně? přeci jen jsem v tomto začínající. Díky :-)
- ch4rli3
- Člen | 6
Zjistil jsem že ani jinde mi to nefunguje (což sám nevím popravdě proč), ale množné číslo být dle výkladu nette nemusí. Nicméně obešel jsem to způsobem viz níže a funguje to, sice trochu kostrbatě, ale vzhledem k tomu, že to je malá interní aplikace, ve které se tato část moc nebude moc používat, je to jen pro orientaci, tak to stačí.
$grid->addColumnText('package_kind_id', 'Druh obalu')
->setCustomRender(function($item)
{
$packageKinds = $this->products->getTypeOrKind('package_kind');
return $packageKinds[$item->package_kind_id];
});
funkce getTypeOrKind vrací pole ‚id‘, ‚material‘ a z toho si pak vypíšu vlastní hodnotu co potřebuju a funguje to :-)
Editoval ch4rli3 (15. 3. 2016 13:25)
- zac24
- Člen | 41
z dema je patrné, že lze pro hromadnou operaci provést výběr jen na aktivní stránce a při změně stránkování grido výběr zapomene. Nebylo by vhodné vybavid grido smysluplnějším chováním, kdy si výběr na jednotlivých stránkách nějakým klientským mechanismem zapamatuje a umožní tak provést hromadnou operaci nad libovolným výběrem napříč všemi stránkami tabulky ? Zabily by se dvě mouchy jednou ranou, rázem by bylo umožněno provést výběr s pomocí třeba dvou i více po sobě aplikovaných filtrů, vyfiltrovat si pohodlně články jednoho autora, pak druhého, z každého jich pár vybrat a protože grido nezapomene výběr před aplikací nového filtru jako doposud moci pak provést nad oběma hromadnou operaci?
- ZahorskyJan
- Člen | 59
vymak napsal(a):
Ahoj,
měl bych na Vás jeden dotaz. Je nějak možné po provedení inline editace, aby došlo k revalidaci celého řádku, na kterém byla úprava provedena?$that = $this; $grid->addColumnText('name', _('Stav')) ->setEditable() ->setEditableCallback(function ($id, $newValue) use ($that) { $this->dictaphoneRepository->update($id, array('state_id' => $newValue)); $that->redrawControl(); // toto nějak nefunguje return TRUE; }) ->setEditableControl($this->getSelect()) ->setEditableValueCallback(function ($row) { return $row->state_id; }) ->setReplacement($this->dictaphoneRepository->getStateArr()) ->setSortable() ->setFilterText() ->setSuggestion();
Těším se na Vaše odpovědi :).
Existuje nějaké řešení?
- ZahorskyJan
- Člen | 59
Oli napsal(a):
Však jsem psal, tohle nefunguje? Nikdy jsem to nezkoušel, ale fungovat by to mohlo…
->setEditableCallback(function ($id, $newValue) use ($grid) { $this->dictaphoneRepository->update($id, array('state_id' => $newValue)); $grid->reload(); return TRUE; })
Bohužel nefunguje. Myslím si, že je to kvůli tomu, že handleEditable() volá presenter->sendResponse se svým payload a tím to celé ukončí. Napadlo mě upravit si client-side JS aby se po úspěšném uložení zavolal grid jako kdyby uživatel kliknul na tlačítko gridu „hledat“, což by mohlo mít ten efekt. Ale nepřijde mi to úplně správně. Nějaký nápad?
- Oli
- Člen | 1215
setCustomRender
a do nej
public function gridHrefRenderEdit($item, \Nette\Utils\Html $el)
{
if(!$this->user->isAllowed('user', 'edit') && !$this->user->isAllowed($this->user->identity, $item, 'edit'))
{
$el->class = [' hidden'];
}
return $el;
}
Plus samozrejme osetrit pri zpracovani… :)
Editoval Oli (9. 4. 2016 14:59)
- Šaman
- Člen | 2659
Ahoj, dá se nějak nastavit, aby aktivní stránka v paginátoru měla
nějakou unikátní třídu? Teď má jen disabled
, stejně jako
může mít i tlačítko ‚předchozí‘ a ‚další‘. Chci si aktuání
stránku zvýraznit, ale ne ta listovací tlačítka. Díky.
--
A propo, tlačitko zpět, je-li zakázané, sice není odkaz a
, je
to span
, ale přesto má atribut href
.