zkopírováno z https://addons.nette.org/nifty/nifty-grid a dále aktualizováno
Vlastní Grid vytvoříme v samostatné třídě (znovupoužitelnost, přehlednost). Každý Grid dědí od základního Gridu \NiftyGrid\Grid
.
Pro zjednodušení budeme vybírat data přímo z databáze bez modelů. Ve vlastní aplikaci byste použili své modely. Níže uvedené příklady jsou psané pro Nette\Database.
Pro příklady budeme používat následující databázi:
id | category_id | user_id | title | published | status | views |
---|---|---|---|---|---|---|
1 | 10 | 30 | Ford Mustang | 2015-08-12 12:33:45 | 1 | 25 |
2 | 14 | 26 | Chevrolet Camaro | 2015-08-14 10:18:37 | 1 | 18 |
3 | 8 | 18 | Dodge Charger | 2015-08-15 14:45:55 | 0 | 11 |
- V presenteru si vytvoříme továrničku na svůj Grid a do konstruktoru předáme tabulku.
protected function createComponentArticleGrid()
{
return new ArticleGrid($this->context->database->table('article'));
}
- Vytvoříme si třídu s vlastním Gridem. V metodě configure se odehrává veškeré nastavení Gridu. Jako jediný parametr má Presenter, který obdrží automaticky od metody Nette\Application\UI\PresenterComponent::attached().
use \NiftyGrid\Grid;
class ArticleGrid extends Grid{
protected $articles;
public function __construct($articles)
{
parent::__construct();
$this->articles = $articles;
}
protected function configure($presenter)
{
//Vytvoříme si zdroj dat pro Grid
//Při výběru dat vždy vybereme id
$source = new \NiftyGrid\NDataSource($this->articles->select('article.id, title, status, views, published, category.name AS category_name, user.username, user.id AS user_id'));
//Předáme zdroj
$this->setDataSource($source);
}
}
- Nyní už v metodě configure můžeme vytvářet sloupce. Každý název sloupce se musí jmenovat podle názvu sloupce v tabulce (případně aliasu).
$this->addColumn('title', 'Titulek', '250px', 30);
$this->addColumn('username', 'Autor', '100px');
$this->addColumn('published', 'Datum', '100px');
$this->addColumn('status', 'Status', '100px');
$this->addColumn('views', 'Zobrazení', '100px');
První parametr a zároveň jediný povinný je název sloupce. Druhý parametr, label, je záhlaví sloupce. Třetí parametr je šířka sloupce – může být v pixelech i procentech. Poslední parametr je oříznutí textu.
Metoda addColumn vrací instanci třídy NiftyGrid\Components\Column
Vlastní nastavení
//pro vypnutí stránkování a zobrazení všech záznamů
$this->paginate = FALSE;
//zruší řazení všech sloupců
$this->enableSorting = FALSE;
//nastavení šířky celého gridu
$this->setWidth('1000px');
//defaultní řazení
$this->setDefaultOrder("article.id DESC");
//počet záznamů v rozbalovacím seznamu
$this->setPerPageValues(array(10, 20, 50, 100));
//vypnutí řazení na konkrétní sloupec
$this->addColumn('column', 'Column')
->setSortable(FALSE);
//nastavení vlastních šablon
$this->setTemplate('cesta/ke/grid-sablone.latte');
$this->getPaginator()->setTemplate('cesta/k/paginator-sablone.latte');
Výsledná data můžeme před vykreslením ještě různě formátovat. Například u sloupce published by se nám hodil pouze datum. K tomu slouží metoda setRenderer, která má jako parametr anonymní funkci s parametrem konkrétního řádku.
$this->addColumn('published', 'Datum', '100px')
->setRenderer(function($row){return date('j.n.Y', strtotime($row['published']));})
Můžeme vykreslit i html tagy pomocí Nette\Utils\Html::el() Například odkaz na profil autora článku
$this->addColumn('username', 'Autor', '100px')
->setRenderer(function($row) use ($presenter){return \Nette\Utils\Html::el('a')->setText($row['username'])->href($presenter->link("user", $row['user_id']));});
Pokud chceme stylem zvýraznit buňku na základě její hodnoty, můžeme k tomu využít metodu setCellRenderer.
$this->addColumn('views', 'Zobrazení', '100px')
->setCellRenderer(function($row){return $row['views'] > 80 ? "background-color:#E4FFCC" : NULL;});
NiftyGrid\Components\Button
Pro přidání řádkové akce slouží metoda addButton. První parametr je název komponenty a druhý je popisek (HTML atribut title). Text tlačítka nastavíme metodou addText. Dále můžeme nastavit css třídu metodou setClass, odkaz pomocí setLink, target pomocí setTarget a potvrzovací dialog pomocí setConfirmationDialog.
$this->addButton("delete", "Smazat položku")
->setClass("btn btn-danger")
->setText("Del")
->setLink(function($row) {return $this->link("delete!", $row['id']);})
->setConfirmationDialog(function($row){return "Určitě chcete odstranit článek $row[title]?";});
Poznámka:
Problém je s potvrzovacím dialogem (JS funkce confirm()), když nepotvrdíte akci, AJAX požadavek přesto proběhne.
V souboruassets/js/grid.ajax.js
je to jednoduše vyřešeno. Pokud místo něj používáte vlastní script, nezapomeňte tento problém ošetřit. Nejjednodužší řešení je vypnout AJAX (viz níže) u tlačítek s potvrzovacím dialogem.
Pokud bychom nechtěli na akci využít AJAX, například při odkazu na jiný Presenter, použijeme metodu setAjax(FALSE)
$this->addButton(...)
->setAjax(FALSE);
Text může být i HTML kód:
$this->addButton(...)
->setText(Nette\Utils\Html::el('span')->setClass('glyphicon glyphicon-pencil')); // <span class="glyphicon glyphicon-pencil"></span>
Můžeme vytvořit i akci, která bude mit jinou funkci na základě hodnoty řádku. Například akce pro publikování/odpublikování článku.
$this->addButton("publish")
->setClass(function ($row) {return $row['status'] === 1 ? "btn btn-danger" : "btn btn-success";})
->setText(function ($row) {return $row['status'] === 1 ? "Odpublikovat" : "Publikovat";})
->setLink(function($row) {return $row['status'] === 1 ? $this->link("unpublish!", $row['id']) : $this->link("publish!", $row['id']);});
A konečně zobrazení tlačítka můžeme řídit také pomocí anonymní funkce.
$this->addButton("delete")
->setShow(function($row) use ($presenter) {return $presenter->getUser()->isAllowed('Articles', 'delete');})
...
NiftyGrid\Components\Action
Pro přidání hromadné akce použijeme metodu addAction. V metodě setCallback je parametr id, který obsahuje pole s hodnotami id vybraných sloupců.
$this->addAction("publish","Publikovat")
->setCallback(function($id) {return $this->publish($id);});
Pro hromadnou akci můžeme také nastavit potvrzovací dialog.
$this->addAction("delete","Smazat")
->setCallback(function($id) {return $this->delete($id);})
->setConfirmationDialog("Určitě chcete smazat všechny vybrané članky?");
Pro aktivaci řádkové editace je třeba přidat řádkovou akci za pomocí předdefinované konstanty.
$this->addButton(Grid::ROW_FORM, "Rychlá editace")
->setClass("btn btn-primary")
->setText("Edit");
Nyní je již aktivní tlačítko pro editaci, ale ještě nejsou žádné sloupce označené jako editovatelné. K tomu slouží metody, které se zapisují ke sloupci.
//textEditable slouží pro textové i číselné hodnoty
$this->addColumn(...)->setTextEditable();
$this->addColumn(...)->setDateEditable();
//v případě selectEditable se grid automaticky pokusí nastavit defaultní hodnotu na základě pole $values a tím umožní editaci připojených tabulek
$this->addColumn(...)->setSelectEditable(array $values, $prompt)
$this->addColumn(...)->setBooleanEditable();
//pro formátování hodnoty do editovatelného formuláře
$this->addColumn(...)->setFormRenderer($callback);
Pro získání a uložení hodnot musíme nastavit callback v metodě configure pro formulář.
$this->setRowFormCallback(function($values){
//db update, flash message, ..
});
NiftyGrid\Components\GlobalButton
Globální akce je v podstatě globální tlačítko s libovolným odkazem. Definuje se metodou addGlobalButton a použití je podobné, jako u řádkové akce NiftyGrid\Components\Button
$this->addGlobalButton("export", "Vyexportuje data do CSV")
->setClass('btn btn-default')
->setText('Export')
->setLink(function() {return $this->link("export!");}); // všimněte si, že anonymní funkce nemá parametr $row - je to globální akce, není závyslá na žádném řádku;
Nejčastější využití globální akce je pro přidání nového záznamu. Pokud je aktivovaná řádková editace, je přidání nového záznamu velice jednoduché - stačí jako první parametr předat předdefinovanou konstantu ADD_ROW:
$this->addGlobalButton(self::ADD_ROW, "Přidat záznam")
->setClass(...)
->setText(...);
Pro filtrování využíváme následujících 5 metod u sloupce.
setTextFilter();
setNumericFilter();
setDateFilter();
setSelectFilter(array $values, $prompt);
setMultiSelectFilter(array $values);
setBooleanFilter();
V případě použití multiselectu lze použít přiložený javascript
assets/js/grid.multiselect.js
Pokud je v Gridu potřeba sloupec z připojené tabulky a chceme v daném sloupci filtrovat záznamy, použije se metoda u sloupce setTableName(„table.column“).
$dataSource = new NDataSource($articles->select("article.title, user.username AS username"));
...
$this->addColumn("username", "Uživatel")
->setTableName("user.username")
->setTextFilter();
Filtrování textu
zadáno do inputu | filtruje |
---|---|
text | obsahuje "text" |
text% | začíná na "text" |
%text | končí na "text" |
Filtrování čísel a data
zadáno do inputu | filtruje |
---|---|
1 | přesně 1 |
>1 | větší než 1 |
>=1 | větší nebo rovno 1 |
<1 | menší než 1 |
<=1 | menší nebo rovno 1 |
<>1 | rozdílný od 1 |
Autocomplete se nastavuje pro filtr a může být použit pouze s textovým filtrem. Nastavuje se až po filtru.
První parametr je počet vyhledávaných záznamů, druhý nastavuje způsob vyhledávání:
- konstanta
FilterCondition::STARTSWITH
(výchozí) "napovídá" slova začínající vepsaným řetězcem - konstanta
FilterCondition::CONTAINS
"napovídá" slova obsahující vepsaný řetězec - použitelné zejména u jmen/příjmení (Jack Daniels je napovězeno při psaní "jac" i "dan")
$this->addColumn('name', 'Jméno')
->setTextFilter()
->setAutocomplete(15, FilterCondition::CONTAINS);
NiftyGrid\Components\SubGrid
Každý Grid může mit více SubGridů. Každý SubGrid může mít další SubGridy. Přidání SubGridy je velice jednoduché, viz kód:
$this->addSubGrid("comments", "Zobrazit komentáře k článku")
->setGrid(new CommentGridByArticleId($presenter->context->database->table('comment'), $this->activeSubGridId))
->settings(function($grid){
$grid->setWidth("800px");
$grid['columns-title']->setWidth("400px");
})
->setCellStyle("background-color:#f6f6f6; padding:20px;");
V metodě setGrid předáme jinou instanci Gridu, předem vytvořenou, například pro komentáře ke konkrétnímu článku. Druhý parametr Gridu je $this->activeSubGridId, což je id řádku, pro který chceme komentáře zobrazit a automaticky je při otevření SubGridu nastaven. V Gridu CommentGridByArticleId budeme omezovat výběr podmínkou na konkrétní id. Pokud chceme uplatnit nějaké nastavení po inicializaci SubGridu, můžeme použít metodu settings. Pro styl buňky subGridu použijeme metodu setCellStyle.
Takto by mohl vypadat jednoduchý Grid CommentGridByArticleId
use \NiftyGrid\Grid;
class CommentGridByArticleId extends Grid
{
protected $comments;
protected $article_id;
public function __construct($comments, $article_id)
{
parent::__construct();
$this->comments = $comments;
$this->article_id = $article_id;
}
protected function configure($presenter)
{
$source = new \NiftyGrid\NDataSource($this->comments->select('comment.id, title, user.username')->where('article_id = ?', $this->article_id));
$this->setDataSource($source);
$this->addColumn("title", "Titulek");
$this->addColumn("username", "Autor");
}
}
Grid používá defaultně třídu NiftyGrid\Components\Translator
, která implementuje rozhraní Nette\Localization\ITranslator
.
Pro překlad celého gridu stačí nakopírovat někam a přeložit soubor localization/empty.json
.
V gridu je pak potřeba zadat cestu k tomuto souboru pomocí metody NiftyGrid\Components\Translator::setLocalization()
,
kde první parametr je cesta k souboru s překlady, nebo přímo přeložené řetězce v poli, respektive Nette\Utils\ArrayHash
a druhý parametr je funkce pro určení plurálu:
public function __construct()
{
parent::__construct();
$this->getTranslator()->setLocalization('cesta/k/souboru.json', function($n) {
return (($n == 1) ? 0 : ($n >= 2 && $n <= 4 ? 1 : 2));
});
}
nastavení translatoru je potřeba udělat co nejdříve, ideálně v konstruktoru
Defaultní NiftyGrid\Components\Translator
je možné přepsat jakýmkoli translatorem (třídou implementující Nette\Localization\ITranslator
), opět co nejdříve:
public function __construct(Nette\Localization\ITranslator $translator)
{
parent::__construct();
$this->setTranslator($translator);
}