avpretty / grid-bundle
Allows you to quickly display the data of any entity or array in the form of a table.
Installs: 641
Dependents: 0
Suggesters: 0
Security: 0
Stars: 9
Watchers: 1
Forks: 4
Open Issues: 1
Type:symfony-bundle
Requires
- php: >=5.5.0
- symfony/dependency-injection: ~2.3|~3.0
- symfony/framework-bundle: ~2.3|~3.0
- twig/twig: ~1.12|~2
This package is auto-updated.
Last update: 2024-10-25 18:57:57 UTC
README
Overview
Allows you to quickly display the data of any entity or array in the form of a table. This table already contains filtering, sorting and pagination.
So using 9 lines of code you can build something like this
Installation
install the GridBundle
executing this console command in your project:
$ composer require avpretty/grid-bundle
Update your AppKernel.php
:
$bundles = [ new AV\GridBundle\AVGridBundle(), ];
GridBundle contains js
and css
and you need to include them manually.
To use code example below the AsseticBundle must be installed:
{% block stylesheets %} {% stylesheets '@AVGridBundle/Resources/public/css/grid-view.css' %} <link rel="stylesheet" href="{{ asset_url }}" /> {% endstylesheets %} {% endblock %} ... {% block javascripts %} {% javascripts '@AVGridBundle/Resources/public/js/grid-view.js' %} <script src="{{ asset_url }}"></script> {% endjavascripts %} {% endblock %}
NOTE: GridBundle
uses bootstrap framework and jQuery.
Grid View
GridView
is the main component that allows you to create and manage you grid.
Each grid for each entity or array requires new instance of GridView
.
Shortest way to create new grid:
// IN YOUR CONTROLLER $em = $this->getDoctrine()->getManager(); $dataSource = $this->get('av_grid.query_data_source') ->setEntityName(User::class)->setRootAlias('u') ->setDataSource($em->getRepository('User')->createQueryBuilder('u')); $gridData = [ 'dataSource' => $dataSource, ]; return $this->render('index.html.twig', [ 'paginationInstance' => $dataSource->getPagination(), 'gridView' => $this->get('av_grid.grid_view_factory') ->prepareGridView($gridData), ]);
And update your template:
{{ gridView(gridView) }} {{ gridPagination(paginationInstance) }}
DONE!
GridViewBundle
provides a lot of options for configure your grid. Detailed
explanation of sorting, filtering, pagination and columns options are described
in corresponding sections of this documentation.
Extended example:
$gridData = [ 'dataSource' => $dataSource, 'filterEntity' => new User, 'tableCaption' => 'Users', 'emptyCell' => 'no data', 'columns' => [ [ 'label' => 'User ID', 'attributeName' => 'id' ], [ 'label' => 'User Full Name', 'content' => function($userEntity, $rowIndex) { return $userEntity->getFirstName().' '.$userEntity->getLastName(); } ], [ 'attributeName' => 'created', 'filterType' => DateType::class, 'filterFieldOptions' => [ 'widget' => 'single_text' ] ] ] ]; return $this->render('index.html.twig', [ 'paginationInstance' => $dataSource->getPagination(), 'gridView' => $this->get('av_grid.grid_view_factory') ->prepareGridView($gridData), ]);
GridView
options:
Grid filter options:
dataSource
- Provides data for grid. Should implementBaseDataSource
interface.- Type:
BaseDataSource
- Required: yes
- Type:
containerOptions
- List of html attributes that will be applied to grid container.- Type:
array
- Required: no
- Example:
['containerOptions' => ['data-type' => 'grid-container', 'class' => 'outer']]
- Type:
tableOptions
- List of html attributes that will be applied to grid table. Here you can override table css style.- Type:
array
- Required: no
- Example:
['tableOptions' => ['class' => 'table table-hover']]
- Type:
tableCaption
- Grid table caption.- Type:
string
- Required: no
- Example:
['tableCaption' => 'Table Caption']
- Type:
showHeader
- Whether the table header row will be shown.- Type:
bool
- Required: no
- Type:
headerRowOptions
- List of html attributes that will be applied to grid table header row.- Type:
array
- Required: no
- Type:
rowOptions
- Options for grid table rows.- Type:
array
- Required: no
- Type:
filterRowOptions
- List of html attributes that will be applied row that contains filters.- Type:
array
- Required: no
- Type:
emptyCell
- Value that will be used for empty table cell.- Type:
string
- Required: no
- Type:
filterEntity
- Instance of target entity that will be used for creating filter fields.- Type:
object
- Required: no
- Type:
filterUrl
- Target url which will accept filter data. Current route will be used by default.- Type:
string
- Required: no
- Type:
Nested Grid
You can insert one grid into another. To do this you need to render nested grid manually:
// IN CONTROLLER $ext = $this->get('twig')->getExtension(GridExtension::class); $nestedGridData = [ 'dataSource' => $nestedGridDataSource, ]; $renderedNestedGrid = $ext->prepareGridView( $gridFactory->prepareGridView($nestedGridData) ); $gridData = [ 'dataSource' => $dataSource, 'filterEntity' => new User, 'tableCaption' => 'Users', 'emptyCell' => 'no data', 'columns' => [ ... [ 'content' => function($entity, $rowIndex) use ($renderedNestedGrid) { return $renderedNestedGrid; } 'format' => ColumnFormat::RAW_FORMAT ] ] ]; return $this->render('index.html.twig', [ 'paginationInstance' => $dataSource->getPagination(), 'gridView' => $this->get('av_grid.grid_view_factory') ->prepareGridView($gridData), ]);
Grid Filters
By default grid filters are disabled. To enable them add next code to your grid configuration:
$userEntity = new User; $gridData = [ 'filterEntity' => $userEntity, ... ];
Specifying entity instance you allowed to build filter fields.
By default filter fields will be created for each entity attributes. But you also can add filter fields for custom column.
Example:
$gridData = [ 'filterEntity' => $userEntity, 'dataSource' => $dataSource, 'columns' => [ [ 'label' => 'User Name', 'attributeName' => 'name', 'filterType' => TextType::class, ] ], ];
NOTE: If you won't specify filter type then symfony will try to guess it. Concerning
this you might got an error Unable to transform value for property path...
. The
main reason of this is doctrine fields validation so there are two options:
manually specify filter type like shown above or remove validation.
You can get all available filter types at symfony's form documentation as well as filter options.
In case of using ArrayDataSource
you should still specify filter entity (any).
Grid filter options:
filterEntity
- Entity instance that will be used to build filter form.- Type:
object
- Example:
'filterEntity' => new User,
- Type:
filterUrl
- Target url which will accept filter data. Current route will be used by default.- Type:
string
- Example:
'filterUrl' => 'custom-url'
- Type:
Full example for User
entity. We have next fields: firstName
, lastName
and created
.
Let's create search
method in UserRepository
:
... public function search(array $searchParams) { $queryBuilder = $this->createQueryBuilder('u'); if (!$searchParams) { return $queryBuilder; } $firstName = $searchParams['firstName']; $lastName = $searchParams['lastName']; $created = $searchParams['created']; // Date foramt - Y-m-d (in our case) if ($firstName) { $queryBuilder->andWhere('u.firstName LIKE :firstName') ->setParameter(':firstName', '%'.$firstName.'%'); } if ($lastName) { $queryBuilder->andWhere('u.lastName LIKE :lastName') ->setParameter(':lastName', '%'.$lastName.'%'); } // I'm sure you'll have time to write code better than this if ($created) { $queryBuilder->andWhere('u.created BETWEEN :start AND :end') ->setParameter(':start', $created) ->setParameter( ':end', (new \DateTime($created))->modify('+1 day')->format('Y-m-d') ); } return $queryBuilder; } ...
Now let's describe our filters in Controller
:
$gridFactory = $this->get('av_grid.grid_view_factory'); /** @var EntityManager $em */ $em = $this->getDoctrine()->getManager(); $dataSource = $this->get('av_grid.query_data_source') ->setEntityName(User::class) ->setRootAlias('u'); $dataSource->setDataSource( $em->getRepository('User')->search($request->get('User')) ); $gridData = [ 'filterEntity' => new User, 'dataSource' => $dataSource, ]; ... // render template
Now filter form will be displayed and you are able to work with incoming filter data.
Filters with relations
Also you can apply filters to related entities. For example we will be using
User
entity and related Country
entity. Let's create dropdown filter by country.
NOTE!!!: TO BE ABLE TO USE FILTRATION ON RELATED ENTITIES YOU SHOULD SPECIFY THOSE ENTITIES IN YOUR QUERY BUILDER. DO NOT USE PROXY OBJECTS.
Example:
[ 'attributeName' => 'country', 'content' => function($userEntity, $rowIndex) { return $entity->getCountry()->getTitle(); }, 'filterType' => EntityType::class, 'filterFieldOptions' => [ 'class' => Country::class, 'choice_label' => 'title' ] ],
Some explanation to example above: attributeName
is compulsory and it's value
should be specified in User
entity. You also can specify arbitrary value of
attributeName
BUT selected filter value won't be remembered by form field
UNLESS you add this field to target (User
) entity.
Columns
Columns provides simple interface for controlling data displaying within grid.
There are three types of columns and one base interface BaseColumn
:
Column
ActionColumn
CounterColumn
Responsible for rendering common data within grid. This type of column displays all user data such as entities or arrays. There are a bunch of display options that can be customize by user.
For example we will be using QueryDataSource
of User
entity.Our entity
contains next fields: firstName
, lastName
, age
, email
, created
.
Simple example:
// To just display all fields of entity you can omit column configuration 'columns' => [ ['attributeName' => 'firstName'], ['attributeName' => 'lastName'], ['attributeName' => 'age'], ['attributeName' => 'email'], ['attributeName' => 'created'], ]
NOTE: If we just omit column
configuration the result will be the same.
In case of using relations you can use next syntax:
'columns' => [ [ 'label' => 'User Country' 'content' => function($userEntity, $rowIndex) { return $userEntity->getCountry()->getTitle(); } ] ] OR YOU CAN USE SHORT SYNTAX 'columns' => [ [ 'attributeName' => 'country.title' ] ]
Extended example:
'columns' => [ [ 'label' => 'User Name' 'sortable' => false 'attributeName' => 'firstName', 'filterType' => EntityType::class, 'filterFieldOptions' => [ 'class' => User::class, 'choice_label' => 'firstName', 'placeholder' => 'Select name', ] ], [ 'label' => 'User Last Name' 'attributeName' => 'lastName' ], [ 'attributeName' => 'fullName' 'content' => function($userEntity, $rowIndex) { return $userEntity->getFirstName() . ' ' . $userEntity->getLastName(); }, 'filterType' => TextType::class, 'filterFieldOptions' => [ 'mapped' => false ] ], [ 'attributeName' => 'created', 'format' => [ColumnFormat::DATE_FORMAT => 'Y-m-d'] ], [ 'attributeName' => 'email', 'visible' => function() use ($someExternalObject) { return // some permission check logic } ], [ 'label' => 'Custom column', 'content' => function($userEntity) use ($someEntity) { return $someEntity->getDataByUserId($userEntity->getId()); }, 'contentOptions' => ['width' => '100px'] ] ]
Column options:
attributeName
- Entity field name or key name in case of usingArrayDataSource
.- Type:
string
- Example:
'attributeName' => 'lastName'
- Required: yes if there no
content
value
- Type:
label
- Column header label. If not specified thenattributeName
will be used.- Type:
string
- Example:
'label' => 'Column Label'
- Required: yes if there no
attributeName
value
- Type:
encodeLabel
- Whether the header will be encoded. False by default.- Type:
string
- Example:
'encodeLabel' => true
- Required: no
- Type:
content
- Column cell content. This parameter can contain string value or callback function.- Type:
string|callable
- Example:
'content' => 'custom string contetn'
. Callback function example was shown above. - Required: no
- Type:
contentOptions
- List of option that will be applied to content cell.- Type:
array|callable
- Example:
'contentOptions' => ['width' => '100px']
- Required: no
- Type:
format
- Format of column cell data. DefaultColumnFormat::TEXT_FORMAT
- Type:
string
- Example:
'contentOptions' => ['width' => '100px']
- Required: no
- Type:
visible
- Whether the column should be visible. Parameter can accept bool value or callable.- Type:
bool|callable
- Example:
'visible' => false
- Required: no
- Type:
sortable
- Whether the column is sortable.- Type:
bool
- Example:
'sortable' => false
- Required: no
- Type:
filterType
- Filter input field type. Can apply one of Symfony form input types.- Type:
string
- Example:
'filterType' => EmailType::class
- Required: no
- Type:
filterFieldOptions
- List of options that will be applied to filter input field. Text field type options- Type:
array
- Required: no
- Type:
filterOptions
- List of option that will be applied to filter cell.- Type:
array
- Example:
'filterOptions' => ['class' => 'custom-filter-cell-class']
- Required: no
- Type:
Provides simple counter for table rows. Included by default for both data sources
QueryDataSource
and ArrayDataSource
.
Inherits most properties of data column.
Example:
'columns' => [ [ 'service' => 'av_grid.counter_column', 'label' => 'My Custom Label' // # by default ] ]
Action columns
allows to create control buttons for each grid row. For now there
are two types of buttons: show
, edit
. Each button configurable. Action columns
will be included to grid by default IF you are using QueryDataSource
in other
case you should specify it manually.
NOTE: By default all action buttons will be visible and will use the current route. Also there is no need to specify each button manually. They will be displayed by default.
Example:
'columns' => [ [ // Custom configuration 'service' => 'av_grid.action_column', 'buttons' => [ ActionColumn::SHOW => 'http://custom-url.com' ActionColumn::EDIT => function ($entity, $url) use ($router) { return $router->generate('compaign_show', ['id' => 4]); }, ], 'hiddenButtons' => [ ActionColumn::EDIT => function ($entity, $url) { return $entity->getId() == 5; // If true then won't be shown } ] ] ]
Each button has two configurable options: url
, visibility
. Both options
accept both a scalar value and a callback function.
If you are using callback function there are two arguments available:
- entity instance
- default button url
buttons
and hiddenButtons
values are optional. By default for url creation
will be used current route with show
path and edit
respectively.
For example if current url is .../admin/user/
then for show
button will be
generated next url .../admin/user/{userId}/show
until you won't change it.
Column data format
This component allow to apply different format type to Column
data. There are
four available formats:
ColumnFormat::TEXT_FORMAT
- Default column cell data format. Encodes allhtml
,js
ortwig
entities.ColumnFormat::RAW_FORMAT
- No encoding at all. Can be use to apply somehtml
orjs
to column cell data.ColumnFormat::TWIG_FORMAT
- Allows to use anytwig
constructions.ColumnFormat::DATE_FORMAT
- Date formatting. For now there are only two date formats that can be formatted:timestamp (int|string)
,\DateTime
,\DateInterval
.
Example:
'columns' => [ [ 'label' => 'Plain text' 'content' => function ($entity) { return '<script>alert("hello");</script>'; // will be displayed as plain text }, ], [ 'label' => 'Execute JS' 'content' => function ($entity) { return '<script>alert("hello");</script>'; // alert message will be shown }, 'format' => ColumnFormat::RAW_FORMAT, ], [ 'label' => 'Format timestamp' 'content' => function($entity) { // return $entity->getCreated(); // returns \DateTime // return '1484204985'; // simple timestamp }, 'format' => [ColumnFormat::DATE_FORMAT => 'Y-m-d'] ] [ 'label' => 'Twig construction' 'content' => function($entity) { return "{{ 'CUSTOM TEXT'|lower }}"; }, 'format' => ColumnFormat::FORMAT ] ]
DataSource
This entity acts as an intermediary between the grid
and the data source. It is
this component that creates the final query and passes the result of its execution
to the grid
component that works with the BaseDataSource
interface.
There are two entities that implements BaseDataSource
:
- QueryDataSource
- ArrayDataSource
QueryDataSource
QueryDataSource
allows to pass your instance of QueryBuilder
to grid.
Example:
// Get user entity query builder $queryBuilder = $entityManager->getRepository('User')->createQueryBuilder('u'); // Initialize data source $dataSource = $this->get('av_grid.query_data_source') ->setDataSource($queryBuilder) ->setRootAlias('u'); // Read below about root alias // Create custom pagination. OPTIONAL $pagination = $this->get('av_grid.pagination') ->setPageSize(5) ->setTotalCount(50); // Create custom sort. OPTIONAL $sortAttributes = [ 'u.email', // Custom configuration 'name' => [ Sort::ASC => [ 'u.first_name' => Sort::ASC, 'u.last_name' => Sort::ASC ], Sort::DESC => [ 'u.first_name' => Sort::DESC, 'u.last_name' => Sort::DESC ], ], ] $sort = $this->get('av_grid.sort')->setAttributes($sortAttributes); $dataSource->setPagination($pagination)->setSort($sort);
NOTE: There is no need to manually configure sort and pagination for QueryDataSource
.
Each instance of BaseDataSource
is created with sort and pagination with default params.
For example if we did not specify sorting params then all field of User
entity will
be sortable.
Now we can get user
entities with sorting and pagination settings, using fetchEntities
:
// Get all user entities $dataSource->fetchEntities(); // Get entities total count even if we did not call `setTotalCount` of pagiantion component $dataSource->getTotalCount();
NOTE: As mentioned above we should specify entity alias like we did it for User
.
Concerning this we should ALWAYS specify entity alias for QueryDataSource
.
If you won't do this then alias will be taken from entity name and it will be User
.
The reason of this is default sorting configuration that should know full field name.
From the other hand you will use constructions like JOIN
.
ArrayDataSource
ArrayDataSource
allows to pass your array data to grid.
Example:
// Get user entity query builder $userData = [ ['first_name' => 'John', last_name => 'Doe', 'email' => 'john_doe@mail.com'], ['first_name' => 'Jane', last_name => 'Doe', 'email' => 'jane_doe@mail.com'], ['first_name' => 'Mike', last_name => 'Doe', 'email' => 'mike_doe@mail.com'], ]; // Initialize data source $dataSource = $this->get('av_grid.array_data_source') ->setDataSource($userData); // Create custom pagination. OPTIONAL ... // Create custom sort. OPTIONAL ... $sort = $this->get('av_grid.sort')->setAttributes($sortAttributes); $dataSource->setPagination($pagination)->setSort($sort);
Now we can pass our $dataSource
to grid and it will be properly displayed.
NOTE: We did not set alias u
for sorting as in case of QueryDataSource
.
We should use only array keys while working with ArrayDataSource
.
Sort
The sorting component allows you to flexibly set and expand the sorting
parameters for specific attributes. You can use this component with one of the
data source component (QueryDataSource
or ArrayDataSource
).
Let's assume that we have User
entity with next fields:
- firstName
- lastName
Example:
$dataSource = $this->get('av_grid.data_source'); $dataSource->setDataSource( $entityManager->getRepository('User')->createQueryBuilder('u') ); // Simple example $sortAttributes = [ 'u.email', 'u.first_name' 'u.last_name' ] // Extended example: sorting by multiple fields $sortAttributes = [ // Default sort params will be used 'u.email', // Custom configuration 'name' => [ Sort::ASC => [ 'u.first_name' => Sort::ASC, 'u.last_name' => Sort::ASC ], Sort::DESC => [ 'u.first_name' => Sort::DESC, 'u.last_name' => Sort::DESC ], 'default' => Sort::DESC, 'label' => 'Name', ], ] $sort = $this->get('av_grid.sort')->setAttributes($sortAttributes); $dataSource->setSort($sort);
If attribute name specified without any params then default params will be used.
Next example shows how the email
attribute will be interpreted.
[ 'u.email' => [ Sort::ASC => ['u.email' => Sort::ASC], Sort::DESC => ['u.email' => Sort::DESC], ], ]
The attribute names used in the configuration are the names of the entity fields
if you are using the QueryDataSource
and the array keys name if you are using the
ArrayDataSource
.
NOTE: In expanded attribute configuration we used custom field title name
but
specified sortable fields using specified alias u
. In case of short notation
we should specify entity alias like we did it with u.email
.
Attribute options:
Sort::ASC
- Configuration of ascend sorting. This param defines sorting by one or multiply attributes. Although this param defines ascend sorting it's allowed to use both types of sorting for any attributes.- Type:
array
- Required: no
- Example:
Sort::ASC => ['first_name' => Sort::ASC, 'last_name' => Sort::ASC]
- Type:
Sort::DESC
- SeeSort::ASC
- Type:
array
- Required: no
- Example:
Sort::DESC => ['first_name' => Sort::DESC, 'age' => Sort::ASC]
- Type:
default
- Default sort params. This sort type will be used by default.- Type:
string
- Required: no
- Example:
['default' => Sort::DESC]
- Type:
label
- Attribute name that will be used in request string. If label was not defined attribute name will be used.- Type:
string
- Required: no
- Example:
['label' => 'FullName']
- Type:
There are few additional params of Sort
instance that can be configured:
sortParam
- The name of the parameter that will contain the sorting data in the query string. This param can be changed to allow multiply sort instances on single page. Default valuesort
.- Type:
string
- Example:
$sortInstance->setSortParam('customSortParam')
- Type:
separator
- Symbol that uses to separate sort attributes in query string. Default value,
.- Type:
string
- Required: no
- Example:
$sortInstance->setSeparator('.')
- Type:
defaultOrder
- Default sort params.- Type:
array
- Required: no
- Example:
$sortInstance->setDefaultOrder(['email' => Sort::DESC])
- Type:
Relation Sort
As well as filter you can sort by related entities fields. For example we will be using
User
entity and related Country
entity.
NOTE!!!: TO BE ABLE TO USE SORT ON RELATED ENTITIES YOU SHOULD SPECIFY THOSE ENTITIES IN YOUR QUERY BUILDER. DO NOT USE PROXY OBJECTS.
Example:
$sort = $this->get('av_grid.sort'); $sort->setAttributes([ 'countryTitle' => [ Sort::ASC => ['c.title' => Sort::ASC], Sort::DESC => ['c.title' => Sort::DESC], ] ]); $dataSource->setSort($sort);
Some explanation to example above: c
is Country
entity alias
that you should specify in you query builder.
Pagination
Pagination component allows you to easily limit the amount of data displayed on the page. This component consists from three parts:
- Pagination - responsible for the managing of the parameters of limitation and offset of
QueryBuilder
. - PaginationView - responsible for the building of the pagination block. Also this component generates pagination links.
- PaginationExtension - twig extension for rendering pagination block.
Pagination example:
$pagination = $this->get('av_grid.pagination'); // Set amount of items per page $pagination->setPageSize(3); // Or default value will be used $pagination->setTotalCount(12); // required if used beyond `QueryDataSource` context. // Set custom pagination instance to QueryDataSource and override default QueryDataSource pagination config $dataSource->setPagination($pagination);
Using these parameters, the pagination component is able to generate the values of the limit and offset based on the query parameters.
// Let's assume that we have query string: .../admin/user/?per-page=3&page=3 echo $pagination->getLimit(); // 3 echo $pagination->getOffset(); // 6
NOTE: There might be a names conflict. For example one of your routes already uses page
query param name.
In that case you can rename page param name:
$pagination->setPageParam('my-page-counter'); // .../admin/user/?per-page=3&my-page-counter=3
Pagination options:
pageSize
- Number of items per page.- Type:
int
- Example:
$pagination->setPageSize(10)
- Required: no
- Type:
totalCount
- Total number of items.- Type:
int
- Required: yes
- Example:
$pagination->setTotalCount(100)
- Type:
pageParam
- Name of query parameter that contains page number. This param can be changed to allow multiply pagination blocks on single page. Default valuepage
.- Type:
string
- Required: no
- Example:
$pagination->setPageParam('customPageName') // .../?per-page=3&customPageName=3
- Type:
pageSizeParam
- Name of query parameter that contains number of items on page. This param can be changed to allow multiply pagination blocks on single page. Default valueper-page
.- Type:
string
- Required: no
- Example:
$pagination->setPageSizeParam('customPageSize')
- Type:
route
- Name of route. If route was not specified then current route will be used.- Type:
string
- Required: no
- Example:
$pagination->setRoute('post_index')
- Type:
defaultPageSize
- Default number of items per page. Will be used ifpageSize
was not specified. Default 20.- Type:
int
- Required: no
- Example:
$pagination->setDefaultPageSize(50)
- Type:
NOTE: BaseDataSource
instances already contains pagination instance.
So there is no need to specify pagination instance manually and set it to
QueryDataSource
or ArrayDataSource
. Instead default pagination configuration will be used.
To display pagination block call twig extension in you template:
// paginationInstance should be passed to template from controller {{ gridPagination(paginationInstance) }}
gridPagination
extension can apply additional params. ALL PARAMETERS ARE OPTIONAL:
{{ gridPagination( paginationInstance, { 'options' : {'id': 'pagination-block-id', 'data-id': 'some-id'}, 'linkOptions' : {'class': 'class-for-each-link'}, 'showFirstPageLink' : true, 'showLastPageLink' : true, 'showPrevPageLink' : true, 'showNextPageLink' : true, 'nextPageLabel' : '›', 'prevPageLabel' : '‹', 'firstPageLabel' : '«', 'lastPageLabel' : '»', 'maxButtonCount' : 13, 'activePageCssClass' : 'active', 'disabledPageCssClass': 'disabled', 'firstPageCssClass' : 'first', 'lastPageCssClass' : 'last', 'prevPageCssClass' : 'prev', 'nextPageCssClass' : 'next', } ) }}