GithubHelp home page GithubHelp logo

uestla / twigrid Goto Github PK

View Code? Open in Web Editor NEW
17.0 5.0 10.0 510 KB

TwiGrid - datagrid built on top of Nette Framework

Home Page: http://forum.nette.org/cs/13181-twigrid-omlouvam-se

CSS 2.32% JavaScript 17.27% PHP 59.90% Latte 20.51%
php nette datagrid sorting filtering row-actions group-actions pagination inline-editing twitter-bootstrap

twigrid's Introduction

TwiGrid

... is a DataGrid for Nette Framework.

Buy me a Coffee

Demo: https://kesspess.cz/twigrid/
Demo sources: https://github.com/uestla/twigrid-demo

It's based on another (and great) datagrid written by @hrach - https://github.com/nextras/datagrid. My datagrid is hugely inspired by this component and its programming ideas and therefore I would like to give this man a maximum credit and respect :-)

Quickstart

Let's see how many steps do we have to make to create our first datagrid.

  1. Create new project

    composer create-project nette/web-project twigrid-quickstart
  2. Install TwiGrid & client-side assets

    cd twigrid-quickstart
    composer require uestla/twigrid
    yarn add twigrid-datagrid --modules-folder www/vendor

    If you're not using yarn, you can install assets manually by looking into package.json and see required dependencies there.

    We'll then update app/Presenters/templates/@layout.latte to load downloaded assets:

    <!-- app/Presenters/templates/@layout.latte -->
    
    <link rel="stylesheet" href="{$basePath}/vendor/bootstrap/dist/css/bootstrap.min.css">
    <link rel="stylesheet" href="{$basePath}/vendor/twigrid-datagrid/assets/twigrid.datagrid.css">
    <!-- ... -->
    <script src="{$basePath}/vendor/jquery/dist/jquery.min.js"></script>
    <script src="{$basePath}/vendor/bootstrap/dist/js/bootstrap.min.js"></script>
    <script src="{$basePath}/vendor/nette-forms/src/assets/netteForms.min.js"></script>
    <script src="{$basePath}/vendor/nette.ajax.js/nette.ajax.js"></script>
    <script src="{$basePath}/vendor/twigrid-datagrid/assets/twigrid.datagrid.js"></script>
  3. Database

    Download the SQLite3 file from the demo application and place it in app/Model/users.s3db.

    And we'll configure this database to be used by the application:

    # config/local.neon
    database:
    	dsn: 'sqlite:%appDir%/Model/users.s3db'
  4. Create datagrid

    Now it's finally time to create our first datagrid - let's create an app/Grids/UsersGrid.php file. We'll need database connection for data loading, so we inject it properly via constructor.

    // app/Grids/UsersGrid.php
    
    final class UsersGrid extends TwiGrid\DataGrid
    {
    	private $database;
    
    	public function __construct(Nette\Database\Explorer $database)
    	{
    		parent::__construct();
    
    		$this->database = $database;
    	}
    
    	protected function build(): void
    	{
    		// TODO
    	}
    }

    We'll define the datagrid body inside the build() method. Although the table user has many columns, we'll have just some of them in our grid just to make it easy.

    // app/Grids/UsersGrid.php
    
    final class UsersGrid extends TwiGrid\DataGrid
    {
    	// ...
    
    	protected function build(): void
    	{
    		$this->addColumn('firstname', 'Firstname');
    		$this->addColumn('surname', 'Surname');
    		$this->addColumn('streetaddress', 'Street address');
    		$this->addColumn('city', 'City');
    		$this->addColumn('country_code', 'Country');
    	}
    }

    TwiGrid also needs to know what column(s) it should consider as a primary key:

    $this->setPrimaryKey('id');

    And finally we'll tell TwiGrid how to load our users:

    $this->setDataLoader(function () {
    	return $this->database->table('user');
    });
  5. To properly inject our grid into presenters, we'll need to create a factory interface:

    // app/Grids/UsersGridFactory.php
    
    interface UsersGridFactory
    {
    	public function create(): UsersGrid;
    }

    This interface will now be used for automatic factory generation, which is handy - we simply add this definition to config/common.neon:

    services:
    	- implement: UsersGridFactory
  6. Having all of this done, we can now simply inject our grid factory into HomepagePresenter.

    // app/Presenters/HomepagePresenter.php
    
    class HomepagePresenter extends BasePresenter
    {
    	/** @var \UsersGridFactory @inject */
    	public $usersGridFactory;
    }

    Now we'll add the control factory itself:

    // app/Presenters/HomepagePresenter.php
    
    protected function createComponentUsersGrid(): \UsersGrid
    {
    	return $this->usersGridFactory->create();
    }
  7. We're nearly done! Just open app/Presenters/templates/Homepage/default.latte, delete the whole content and replace it with

    {block content}
    	{control usersGrid}
    {/block}

    That's all, folks!

    Now when you'll open the page, you might see something like this:

    Result screenshot

  8. Final improvement

    Maybe showing the country code isn't that sexy - we'd like to have the whole country name in "Country" column. To achieve that, we'll create custom grid template:

    {* app/Grids/UsersGrid.latte *}
    
    {extends $defaultTemplate}
    
    {define body-cell-country_code}
    	<td>{$record->country->title}</td>
    {/define}

    And tell TwiGrid to use this template:

    // app/Grids/UsersGrid.php::build()
    
    $this->setTemplateFile(__DIR__ . '/UsersGrid.latte');

    Simple as that!

More

To see more examples, please visit the demo page. Enjoy!

twigrid's People

Contributors

martinmystikjonas avatar uestla avatar vlczech avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

twigrid's Issues

More than one grid at presenter

fix

	protected function attached($presenter){
		if($presenter instanceof NPresenter){
			$this->build();
			parent::attached($presenter);
			$this->session = $presenter->getSession(__CLASS__ . '-' . $this->getName());

			if(!isset($presenter->payload->twiGrid)){
				$this->payload = $presenter->payload->twiGrid = new \stdClass();
				$this->payload->forms = [];
			}else{
				$this->payload = $presenter->payload->twiGrid; // this is fix
			}
		}
	}

end() expects parameter 1 to be array, null given

Pokud chci využívat filtry, a nesplním jejich podmínky, vypíše se alert (správně), ale pokud projde vstup input buttonu přes form rules a je hodnota validní, vypíše to chybu end() expects parameter 1 to be array, null given na řádku označeném níže. Využívám TwiGrid již dlouho, několik chyb jsem po po 2.4RC odstranil a zašlu pak patch, každopádně přes tuto chybu se dostat ani já nedokáži.

{define filter-cell}

    <th n:class='filter-cell, ($hasControl = isset($form["filters-criteria-$column"])) && !($isControl = ($form["filters-criteria-$column"] instanceof Nette\Forms\IControl)) ? alert-warning'>
    {if $hasControl && $isControl}
        {php }{input filters-criteria-$column, class => 'form-control'} <--- Zde
    {else}
        &nbsp;
    {/if}
    </th>

{/define}

Každopádně, pokud se s aktuální verzí vyzkouší filtrování, deprecated problémů je opět více. Např. co vyhodí User Notice:

value => $template->getValue($record, $name, FALSE)

či toto (Accessing parameters via. $template->param is deprecated, nyní v Nette/Latte neexistuje způsob jak v Latte nastavit parametr, jedině skrze renderBlock).

{var $template->hasData = (bool) $iterations}

Chybějící buňka v patičce

Ahoj, když si přidám inline edit, tak se rozhodí rozložení tabulky, resp. v patičce chybí buňka posledního sloupce.

twigrid

Localization of "No data." message

Hi!
Currently the message "no data" is showing directly in body block. If we want to change this message, there is need to rewrite the whole body block (around 60 lines of code) just to use something like _'admin.grid.empty'.

Can you split the body block into smaller pieces? Or at least define new block for the message? Something like

{define body-empty}

Or an option for defining empty message for the given grid? Something like

$grid->setEmptyMessage('admin.grid.empty');

Or do you have any other idea how to deal with this?

Possibility to wrap header to multiple lines.

Would be great if will be possible do this

$this->addColumn('area', "Plocha bytu\n[m²]");

Currently it is possible by customize template, but maybe something like this should be supported in the default one, just by adding |breaklines

Possibility to decorate rows/cells

Only thing that I miss most is possibility to decorate rows and cell.
I mean mainly possibility to add custom class per row or cell in similar way how ValueGetter is done.
Ideally some interface with several methods which will be called for every row or cell and allows to ad class there or completely change content of the desired cell, like add some html element into it.

Strange behavior of multiple sort

There is a strange behavior of multisorting:
I click a "+" button on name for example (this column is sorted first), then click "+" button" on surname for example (this column is sorted second). There are "+" buttons on these columns still.

Now I would like to change sort the name column (firstly sorted column) from ASC to DESC order. So let's try it by click to:

  • column name => oh no, this is single sort and my previous multi-sort was killed.
  • displayed sorting arrow => this does not function (however this icon does make sense to use for change the order of sort; for example with 180° rotated icon on hover)
  • "+" button => OK, the order of sort is changed.

And what about to change back the name column from DESC to ASC order preserving firstly sorting?

  • OK, i know: "+" changed me in the previous step => click => oh no, my first sort is now on surname only and previous name column sort by first was killed (if I had only 2 multisorts). Actually the "+" button has no function of add multiple sort, but has the function of drop current column from multiple sort realistically.

Proposed solution:

  • "+" button is used only to add the column to multisort
  • for changing between ASC and DESC order is used "^" or "v" icon hovered to opposite site
  • after the column is added to multisort "+" icon is replaced by "x" button to cancel sort on this column

EDIT:
For consideration:

  • after multisorting is activated, click on column label has the meaning of change the order instead of cancel multisort order to column-only order

Summary row at the end of the Grid

I often need some summary row at the end of the grid with total or average values.
I'm doing it usually by adding one more row with the special class to style it differently.
But I don't found a way how to handle it if I have some special formatting rule for one column, which should not apply to the last row.

Like

{define body-cell-month-content}
    <a href="{plink :month $record->year, $record->month}">{$$recordVariable |getValue:$columnName:FALSE}</a>
{/define}

The last row should not be clickable. Is there some official way how to do it?

"multisort" class in all headers

In latest version 11.2.1 is one strange thing.
All column headers has class 'multisort' also when column doesn't have allowed sorting by setSortable(), so all columns has additional space on the right, but without sorting icon, which looks weird, if you are not using sorting. I found that it is possible turn it of by setMultiSort(false).

build() is called too early in component lifecycle

I need to react on a signal by handleDisplay($year, $month) method, but it is not possible, because build() method is evaluated before a signal is processed. It is the correct behavior? I would expect that would be possible to change the grid configuration based on signals.

composer dependency forcing to download 2.4RC1

"require" : { "php": ">= 5.3.0", "nette/http": "^2.3", "nette/forms": "^2.3", "nette/utils": "^2.3", "nette/application": "^2.3"

To this:
"require" : { "php": ">= 5.3.0", "nette/http": "~2.3", "nette/forms": "~2.3", "nette/utils": "~2.3", "nette/application": "~2.3"

Cause it forces automatically to download 2.4RC1

Compatibility with PHP 7.0.19

Latest version currently available for Raspberry Pi is 7.0.19.

I tried latest version and it crash just because of "?" used here public function getColumns(): ?\ArrayIterator or public function getItemsPerPage(): ?int. This is some new syntax of 7.1? I did not found any information about it yet. But without it, it normally works on booth versions.

@deprecated method accessing

Because of new Nette SmartObject strict is really important to change callback calls from ($this->function) to callback($this, 'function').

For ex:
$latte->addFilter('translate', callback($this, 'translate'));

Unable to render DataGrid inside snippet using AJAX

We have DataGrid control inside snippet. If we make AJAX request which invalidates this snippet DataGrid ens in fatal error. Reason is passForm() mechanism. Form is not passed to template and it fails to render.

If we remove passForm conditions from render method (currently line 871) eberything works as expected.

What is purpose of this condition?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.