Platform 2 Launched! Demo, Videos | Laravel 5 Support Begins! Announcement

Cartalyst LLC.
Data-grid by Cartalyst
7
68
132
16
6

This package requires a valid subscription. Subscribe for access.

Introduction

A framework agnostic data grid package that makes it easy to filter large data sources. It shifts the focus from pagination to data filtration. Pass any data source through a data handler and the package will take care of the rest so you can use the filtered result set to create your applications.

The package requires PHP 5.3+ and comes bundled with a Laravel 5 Facade and a Service Provider to simplify the optional framework integration and follows the FIG standard PSR-4 to ensure a high level of interoperability between shared PHP code and is fully unit-tested.

Have a read through the Installation Guide and on how to Integrate it with Laravel 5 or Native Integration.

Quick Example

$data = Post::all();

return DataGrid::make($data, array(
    'id',
    'slug',
    'title',
));

Installation

The best and easiest way to install the Data Grid package is with Composer.

Preparation

Open your composer.json file and add the following to the require array:

"cartalyst/data-grid": "3.0.*"

Add the following lines after the require array on your composer.json file:

"repositories": [
    {
        "type": "composer",
        "url": "https://packages.cartalyst.com"
    }
]

Note: Make sure that after the required changes your composer.json file is valid by running composer validate.

Install the dependencies

Run Composer to install or update the new requirement.

php composer install

or

php composer update

Now you are able to require the vendor/autoload.php file to autoload the package.

Optional

Data grid provides optional support for pdf downloads.

In order to download your results as pdf files, you must require the dompdf library or your composer.json file.

"dompdf/dompdf": "dev-develop"

Integration

Laravel 5

The Data Grid package has optional support for Laravel 5 and it comes bundled with a Service Provider and a Facade for easy integration.

After installing the package, open your Laravel config file located at config/app.php and add the following lines.

In the $providers array add the following service provider for this package.

'Cartalyst\DataGrid\Laravel\DataGridServiceProvider',

In the $aliases array add the following facade for this package.

'DataGrid' => 'Cartalyst\DataGrid\Laravel\Facades\DataGrid',

Configuration and assets

After installing, you can publish the package configuration and assets into your application by running the following command on your terminal:

php artisan vendor:publish

This will publish the config file to config/cartalyst.data-grid.php where you can modify the package configuration. and the assets are published to public/assets/cartalyst/data-grid/*

Data Handlers

By default, the package will register two built-in data handlers with Laravel, the Cartalyst\DataGrid\DataHandlers\CollectionHandler and the Cartalyst\DataGrid\DataHandlers\DatabaseHandler.

Note: You can register more data handlers by publishing and editing the config file.

Native

Integrating the package outside of a framework is incredible easy, just follow the example below.

// Include the composer autoload file
require_once 'vendor/autoload.php';

// Require the data grid config file
$config = require_once 'vendor/cartalyst/data-grid/src/config/config.php';

// Instantiate and configure the envrionment
$dataGrid = new Cartalyst\DataGrid\Environment(null, $config['handlers']);

$data = array(

    array(
        'title' => 'bar',
        'age'   => 34,
        'desc'  => 'a description here',
    ),

    array(
        'title' => 'acme',
        'age'   => 20,
        'desc'  => 'a description here',
    ),

    array(
        'title' => 'foo',
        'age'   => 12,
        'desc'  => 'a description here',
    ),

);

// Make the grid
return $dataGrid->make(
    $data,
    array(
        'title',
        'age',
        'desc',
    ),
    array(
        'sort'      => 'age',
        'direction' => 'desc',
    )
);

Note: We are registering two default data handlers by passing the $config['handlers'] array as a second param into the Environment constructor call, you can register more data handlers by passing in a different array of handlers.

Upgrade guide.

2.x to 3.x

  1. Open your composer.json file and update the data-grid version to 3.0.* and run composer update.
  2. Publish the config file and assets using php artisan vendor:publish and make any required config changes on the newly published file.

Usage

In this section we'll show how you can utilize the data grid package.

Cartalyst's Data Grid package provides a couple of ways to interact with. The most basic way is to instantiate a new environment with the Cartalyst\DataGrid\Environment class and use Cartalyst's default built-in Cartalyst\DataGrid\DataHandlers\CollectionHandler for data handling.

After creating a Data Grid object you can use the registered data handler to interact with your result set.

Creating a Data Grid Object

Creating a Data Grid object can be done by calling the make function on the Data Grid environment.

$dataGrid = $environment->make($data, $columns);

Calling the make function will send back an instance of Cartalyst\DataGrid\DataGrid. The $data variable must contain all of the data you want to filter. This can be any sort of data type as long as it can be handled by your data handlers. The $columns variable must contain an array of all the columns for each data object to include in the result set.

The data provided can hold data objects of the following types:

  • An array
  • An object which is an instance of or extends the stdClass object
  • An object which implements the Illuminate\Support\ArrayableInterface interface

A basic example of creating a Data Grid object could be:

$object = new StdClass;
$object->title = 'foo';
$object->age = 20;

$data = array(
    array(
        'title' => 'bar',
        'age'   => 34,
    ),
    $object,
);

$dataGrid = $environment->make($data, array(
    'title',
    'age',
));

Because we send in the data wrapped in an array, the Data Grid object will handle the data with the registered CollectionHandler Data Handler.

Note: If a data object in the $data set doesn't have a column set in the $columns array, it will return null in the result set for that column.

You can also rename columns by defining them as a key/value pair with the originial name being the key and the new name being the value.

$dataGrid = $environment->make($data, array(
    'title' => 'new_title_column_name',
    'age'   => 'new_age_column_name',
));

Options

Options can be passed as a third array to the make method.

$dataGrid = $environment->make($data, array(
    'title',
    'age',
), array(
    'sort'      => 'age',
    'direction' => 'desc'
));

Below is a list of available options

Option Type Default Description
sort string null Default sort if no sort has been provided.
direction string null Default direction if no direction has been provided.
method string null Default method if no throttle has been provided.
throttle int null Default throttle if no throttle has been provided.
threshold int null Default threshold if no throttle has been provided.
pdf_view string cartalyst/data-grid::pdf Pdf view that renders the results for pdf download.
pdf_filename string data-grid Pdf filename for pdf download.
json_options int null Json options ex. JSON_PRETTY_PRINT for json download.
json_filename string data-grid Json filename for json download.
csv_delimiter string ',' Csv delimiter for csv download
csv_filename string data-grid Csv filename for csv download.
csv_parser function null Csv parser, define your own parser for csv download.
max_results int null Maximum results for downloads.

Note: By default, all filtered results are included in the download. Pdf rendering is a resource intensive process, therefore it might take a long time to render a large set of results.

Catching Unsupported Data Types

When the Data Grid package can't find a Data Handler for the provided data, it will throw a RuntimeException*. You can catch it by doing the following:

try
{
    $dataGrid = $environment->make($data, $columns);
}
catch (\RuntimeException $exception)
{
    echo $exception->getMessage();
}

* PHP manual on the RuntimeException class

Environment

Before you can use the Data Grid package you need to load a new environment first. This environment will determine which request provider it needs to instantiate for you to interact with. It will load an instance of Cartalyst\DataGrid\RequestProviders\Provider by default.

Loading An Environment

$environment = new Cartalyst\DataGrid\Environment;

From here on, you can start working with the Data Grid package.

You can register your custom request provider by sending it along when instantiating a new environment.

$provider = new CustomProvider;

$environment = new Cartalyst\DataGrid\Environment($provider);

Note: Make sure that your request provider implements Cartalyst\DataGrid\RequestProviders\ProviderInterface.

Data Handlers

The data handler is the class that handles and filters the data you passed along. In the examples below we'll go over the functionality of the data handler.

Registering Data Handlers

Data handlers are essentially drivers which manipulate a data source and return the required data. You can register data handlers with your environment by using the setDataHandlerMapping function.

$environment->setDataHandlerMapping('FooDataHandler', function($data)
{
    return ($data instanceof FooData);
});

Now whenever you pass along data which is an instance of FooData when instantiating the Data Grid, the package will know to use the FooDataHandler to handle the data.

Alternatively you can register your data handlers when loading an environment.

$handlers => array(

    'FooDataHandler' => function($data)
    {
        return ($data instanceof FooData);
    },

    'BarDataHandler' => function($data)
    {
        return ($data instanceof BarData);
    },

);

$environment = new Cartalyst\DataGrid\Environment(null, $handlers);

Default Data Handlers

Collection Handler

The collection handler can handle the following types of data.

  • Illuminate Collection objects
  • Arrays with data objects which could be:
    • An array
    • An object which is an instance of or extends the stdClass object
    • An object which implements the Illuminate\Support\ArrayableInterface interface

If you'd like to use the CollectionHandler data handler you need to register it to your Data Grid environment.

$environment->setDataHandlerMapping('Cartalyst\DataGrid\DataHandlers\CollectionHandler', function($data)
{
    return(
        $data instanceof Illuminate\Support\Collection or
        is_array($data)
    );
});

Now whenever you pass along an array of data or an Illuminate\Support\Collection object when instantiating the Data Grid, the package will know to use the CollectionHandler to handle the data.

Database Handler

The database handler can handle the following types of data.

  • Queries
  • Query results
  • Eloquent Models & Relationships

If you'd like to use the DatabaseHandler data handler you need to register it to your Data Grid environment.

$environment->setDataHandlerMapping('Cartalyst\DataGrid\DataHandlers\DatabaseHandler', function($data)
{
    return (
        $data instanceof EloquentModel or
        $data instanceof EloquentQueryBuilder or
        $data instanceof HasMany or
        $data instanceof BelongsToMany or
        $data instanceof QueryBuilder
    );
});

Now whenever you pass along any of the above defined data types objects when instantiating the Data Grid, the package will know to use the DatabaseHandler to handle the data.

Custom Data Handlers

In addition to register the default data handlers provided by the package, you can create your own custom data handlers as well. All data handlers need to extend the abstract Cartalyst\DataGrid\DataHandlers\BaseHandler class.

use Cartalyst\DataGrid\DataHandlers\BaseHandler;

class CustomHandler extends BaseHandler {

}

Specific handlers can be created to handle specific sets of data like framework specific result sets or a certain service's API result responses.

Accessing the registered data handler can be done by calling the getDataHandler method on the Data Grid object.

$handler = $dataGrid->getDataHandler();

This will return an array with the result set after all the request parameters have been applied.

Other methods

Get the total amount of results.

$totalCount = $handler->getTotalCount();

Get the total amount of filtered results.

$filteredCount = $handler->getFilteredCount();

Get the current page.

$page = $handler->getPage();

Get the number of pages.

$pagesCount = $handler->getPagesCount();

Get the previous page.

$previousPage = $handler->getPreviousPage();

Get the next page.

$nextPage = $handler->getNextPage();

Get the number of results per page.

$perPage = $handler->getPerPage();

Result Sets

After instantiating a Data Grid object you have a couple of options to work with the data result sets. Depending on the request provider which you registered with your environment, the implementation of these methods can differ. In the examples below we're going to assume you're using the default Cartalyst\DataGrid\RequestProviders\Provider request provider.

You can filter results by sending specific request parameters along with your HTTP request. Your request provider will catch these so your data handler can filter the data based on these request parameters.

Generating Results

You can convert the result set from the Data Grid object to an array or JSON response by calling the toArray or toJson functions.

$environment = new Cartalyst\DataGrid\Environment;

$dataGrid = $environment->make($data, $columns);

// Retrieve the result set as an array.
$array = $dataGrid->toArray();

// Retrieve the result set as a JSON response.
$json = $dataGrid->toJson();

The returned response would look something like this:

{
    "total": 3,
    "filtered": 3,
    "page": 1,
    "pages": 1,
    "previous_page": null,
    "next_page": null,
    "per_page": 3,
    "sort": "name",
    "direction": "asc",
    "default_column": "name",
    "results": [
        {
            "name": "John Doe",
            "age": 22,
            "location": "New York"
        },
        ...
    ]
}

The response contains some useful information like the total number of results, the amount of filtered results, the current, previous and next page and a list with all of the results for the current page.

Note: When sending the Data Grid object to the browser, it will be automatically converted to a JSON response. This is very useful, for example, when building APIs.

Sorting Results

You can sort the result set by sending a request parameter with the sort key.

http://example.com/search?sort=name

This will sort the results by name.

Reverting the sorted results can be done by sending a request parameter with the direction key.

http://example.com/search?sort=name&direction=desc

Now the results will be sorted descended by name.

Switching Pages

Changing the page in a result set can be done by sending a request parameter with the page key.

http://example.com/search?page=2

This would show the results on the second page of the result set.

Filter Results

You can filter results by sending a request parameter with the filters key. The filters request parameter must provide filters based as a key (column) and value (column value) array.

For example:

http://testing.loc/search?filters[name]=foo&filters[age]=24

This would show only results which have a foo name and an age of 24.

Paginating Results

There are three request parameters you can use to paginate a result set: method, threshold and throttle.

method is the default method of pagination, single, group or infinite are allowed values, single will paginate based on a results per page (throttle value), group will paginate based on pages per results (throttle value) meaning it will try to paginate your resultset into this number of pages.

threshold is the number of results before pagination is applied to the result set. For example: if you set this to 5 then only when there are more than 5 results, pagination would be applied. The default for this parameter is 100.

throttle is the maximum amount of results you want to show on a single page incase of using the default single method, when using the group method this value will refer to the ideal number of pages to show based on the results.

An example of these request parameters can be:

http://example.com/search?threshold=5&method=group&throttle=10

In this example, the data handler will start paginating the results when there are more than 5 results. When there are less than 100 results, pagination will be created with 10 pages containing a number of results for each page based on dividing the total number of results by 10 (throttle).

Javascript Plugin

Introduction

One of our goals with Data Grid was to leave the front end HTML up to you, and avoid what most plugins do by forcing you into a specific container. We built a Javascript plugin (data-grid.js) that works together with the Underscore.js rendering engine to allow you to easily build flexible data grids.

Requirements

Using data-grid.js requires the following:

Installation

Add jQuery, Underscore.js and data-grid.js to the <head> section of your page.

<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="packages/cartalyst/data-grid/js/underscore.js"></script>
<script src="packages/cartalyst/data-grid/js/data-grid.js"></script>

Note Make sure you publish the assets using php artisan asset:publish cartalyst/data-grid or update the paths accordingly.

The HTML

Data Grid requires three elements for instantiation: a results container, pagination container and a applied filters container. Each of these containers will require a corresponding underscore template that is responsible for rendering the markup.

Note: The data-template attribute is required on all templates for identification by the script.

Underscore templates

The following underscore templates are required for every data grid and are defined using the data-template attribute.

Data template Description
results Renders the results.
no_results Renders if no results are returned.
filters Renders applied filters.
pagination Renders pagination.

Results container

<table class="results" data-grid="main" data-source="http://example.com/api/v1/foo">

    <thead>

        <tr>

            <td>City</td>
            <td>Population</td>

        </tr>

    </thead>

    <tbody></tbody>

</table>

Results template

<script type="text/template" data-grid="main" data-template="results">

    <% _.each(results, function(r){ %>

        <tr>

            <td><%= r.city %></td>
            <td><%= r.population %></td>

        </tr>

    <% }); %>

</script>

No results template

<script type="text/template" data-grid="advanced" data-template="no_results">
    <tr>
        <td colspan="4">No Results</td>
    </tr>
</script>

The required data-grid attribute will allow you to create multiple Data Grids on a single page and the results class will mark it as the results container. The data-source attribute contains the API endpoint URI. If your results container is a table, we will automatically render the template in the <tbody> element.

You might notice that the <% ... %> is the default underscore brace syntax. You can always change this behaviour by changing the template_settings on the plugin's options.

Pagination container

<ul class="pagination" data-grid="main"></ul>

Pagination template

<script type="text/template" data-grid="main" data-template="pagination">

    <% _.each(pagination, function(p) { %>

        <li data-grid="main" data-page="<%= p.page %>"><%= p.page_start %> - <%= p.page_limit %></li>

    <% }); %>

</script>

Because we're setting the same data-grid attribute, the plugin will know to group it with your results container. We use the pagination class to indicate it as the pagination container.

As for the other attributes, the data-page attribute is where we store the current page. By default we will use page_start and page_limit in our pagination template to indicate which results are displayed on each page. This would output to 1 - 10, 11 - 20, ...

Filters container

<ul class="filters" data-grid="main"></ul>

Filters template

<script type="text/template" data-grid="main" data-template="filters">

    <% _.each(filters, function(f) { %>

        <li>
            <% if(column === 'all') { %>

                <%= f.value %>

            <% } else { %>

                <%= r.value %> in <%= f.column %>

            <% } %>
        </li>

    <% }); %>

</script>

We check for columns so you can display your filters in a readable manner. If your filter isn't filtering within a column it will just display the filter. If your filtering within a column we show both the filter and column.

The Javascript

Now that you have all of your templates setup, let's instantiate Data Grid. The first argument within the instantiation is the grid, this is the value of the data-grid attribute. This allows us you to have a flexible layout, and multiple Data Grids on a page. Next is the results container for the response, followed by the pagination container and finally the applied filters container.

$.datagrid(grid, results, pagination, filters, options)
<script>

    $(function()
    {
        $.datagrid('main', '.results', '.pagination', '.filters');
    });

</script>

Should you have placed the data-source attribute on the results container, the plugin will automatically know which URI to make it API calls to. You can also set the URI through the plugin's options.

After applying the plugin, you should get a nicely filled table with all of your data and pagination.

Filters

Lets go over how to set filters now that you have your Data Grid returning results. We use the data-filter attribute to define filters.

Note: Filters can be set on any html element on your page.

Regular filters

Regular filters can be added on elements using a column:value pair

<button data-filter="all:USA" data-grid="main">Filter USA</button>

Multiple filters

Multiple filters can be added on one element that are then applied as a batch, filters must be separated by , using column1:value1, column2:value2, column3:value3

Note: The space after the comma is required.

<button data-filter="all:USA, id:1:10" data-grid="main">Filter USA</button>

Range filters

Range filters can be applied using column:start_value:end_value

<button data-filter="id:1:10" data-grid="main">ID 1 - 10</button>

Dropdown select filters

<select data-select-filter data-reset>
    <option>All</option>
    <option data-filter="country:germany">Germany</option>
    <option data-filter="country:france">France</option>
    <option data-filter="country:taiwan">Taiwan</option>
</select>

Note 1: Adding a data-reset attribute on the select element will clear all filters that are part of this select element before applying a new one while leaving other filters untouched.

Note 2: Adding a data-reset attribute on the first option element will clear all filters on the grid when selecting All

Date range filters

Data grid supports date range filters that allow filtering based on a data-range-start attribute to a data-range-end attribute.

<input type="text" data-format="DD MMM, YYYY" class="form-control" data-range-start data-range-filter="created_at" data-label="Created At">

<input type="text" data-format="DD MMM, YYYY" class="form-control" data-range-end data-range-filter="created_at" data-label="Created At">

Note 1: This example uses moment.js for the data-format to work, this is responsible for converting the date to the database date format before applying the filters.**

Note 2: You can use any date plugins with date range filters, just make sure either the data-format attribute is present or change it to a different attribute by passing "date_format_attribute": "date-format" to the data grid inititalization object to use a different attribute, you only pass the part after data- not the entire name. Therefore, this would require a data-date-format attribute.

Other filter options

Grouping filters under one data-grid attribute

You can omit the data-grid attribute from filter elements if the parent element already has it set.

<div data-grid="main">

    <button data-filter="country:us">United States of America</button>
    <button data-filter="country:ca">Canada</button>
    <button data-filter="country:uk">United Kingdom</button>

</div>
Reset all other filters

You can add a data-filter-reset attribute to any filter element to force clearing other filters before applying the current filter.

<button data-filter="all:us" data-filter-reset data-grid="main">USA</button>

This will clear any already applied filters before applying the us filter.

Reset filters based on a group

You can add a data-filter-reset attribute on a parent element of all filters you want cleared, it acts as grouped filters that force clear all filters belogning to this group before applying a new one.

<div data-grid="main" data-filter-reset>

    <button data-filter="id:1">ID 1</button>
    <button data-filter="id:2">ID 2</button>
    <button data-filter="id:3">ID 3</button>

</div>

This will clear only filters that are part of this group while leaving other filters untouched.

Sorts

<a href="#" data-sort="city:asc" data-grid="main">Sort By City ASC</a>

This will sort the data grid by city in a ascending order.

<a href="#" data-sort="city:desc" data-grid="main">Sort By City DESC</a>

This will sort the data grid by city in a descending order.

Labels

From time to time, when using filters and sorts you will run into issues when your column names have underscores or something of that nature. Because of this we've created the data-label attribute to help you rewrite this to achieve a better user experience. The data-label works similar to filters, it takes the form original_column:new_column:new_value, you can also combine multiple labels to match multiple filters on one data-label attribute.

<a href="#" data-filter="country:us, subdivision:washington, population:<:5000" data-label="country:Country:United States, subdivision:Subdivision:Washington, population:Population:5000">Washington, United States < 5000</a>

Search

Data Grid ships with a few other things to help developers get off the ground faster. If you are looking to search within your data set, all you need to do is create a form and set the data-grid attribute and set data-search. Make sure the input name is set to filter.

<form method="post" action="" accept-charset="utf-8" data-search data-grid="main">

    <input name="filter" type="text" placeholder="Filter All">

    <button>Add Filter</button>

</form>

Now if you are looking for to let users filter within defined columns all you need to do is add a select menu within the form. The select name should be set to 'column' and the value of options set to the column name.

<form method="post" action="" accept-charset="utf-8" data-search data-grid="main">

    <select name="column" class="input-medium">

        <option value="all">All</option>
        <option value="city">city</option>
        <option value="population">Population</option>

    </select>

    <input name="filter" type="text" placeholder="Filter All">

    <button>Add Filter</button>

</form>

Note: You can enforce strict equality on the search form using data-operator="=" on the form element.

Reset

If you want to give your users a simple way to reset Data Grid, just create a button, set the attributes data-grid and data-reset and your done.

<button data-reset data-grid="main">Reset</button>

Options

You can specify plugin options by adding a fifth object parameter to your plugin instantiation.

<script>
    $(function()
    {
        $.datagrid('main', '.results', '.pagination', '.filters',
        {
            source: 'http://example.com/api/v1',
            sort: {
                column: 'city',
                direction: 'desc'
            },
            ...
        });
    });
</script>

Below is a list with all of the available options.

Option Type Description
source string The API endpoint URI.
threshold integer Minimum amount of results before pagination is applied.
throttle integer The maxmim amount of results on a single page. Overrides dividend.
method string The pagination method, accepts single, group or infinite.
sort object Set a default sort, by applying column and direction attributes.
sortClasses object Set the CSS classes to apply to [data-sort] objects by changed the asc and desc attributes.
delimiter string Set a different delimiter for the hash url.
dateFormatAttribute string Set the data attribute for date formats.
templateSettings object Changes the surrounding braces. Data Grid's default is set to <% ... %>.
scroll mixed Set an element selector to scroll to or a function to apply manual scrolling.
infinite_scroll bool Enables infinite scroll.
searchTimeout number Set the threshold for the live search feature. (Time after last keystroke before search starts)
hash bool enabled or disable hash urls.
loader string class or id of a loading element to be shown while the ajax request is made.
callback function This parameter you can pass a function that will run every time a filter is added, or a sort is applied. This function recives one argument, and gives you access most values within Data Grid.
events object This parameter you can pass an object with events and functions to run custom logic after specific actions are called.

Events

Below is a list with all of the events that are fired throughout the lifecycle.

Event Description
applying Fired before a filter is applied.
applied Fired after a filter has been applied.
applying_default Fired before a default filter is applied.
applied_default Fired after a default filter has been applied.
removing Fired before a filter is removed.
removed Fired after a filter has been removed.
sorting Fired before sorting is applied.
sorted Fired after sorting has been applied.
fetching Fired before triggering the ajax fetch request.
fetched Fired after a successful ajax fetch request.
switching Fired before the page changes.
switched Fired after the page has changed.

Example

var grid = $.datagrid('single', '.table', '.pagination', '.filters', {
    events: {

        'applying': function(obj) {
            // Run custom logic here
        },

    }
});

Extending

This plugin was built with extensibility in mind,

You can override or extend any method using the following syntax.

$.datagrid.prototype.{method} = function ({params}) {

    // Your logic

};

Hooking into an existing method

You can hook into existing methods to apply custom logic before the method is invoked.

var original_method = $.datagrid.prototype.getThreshold;

$.datagrid.prototype.getThreshold = function () {

    // Your custom logic
    return original_method.call(this);

};

Overriding an existing method

You can override existing methods.

$.datagrid.prototype.getThreshold = function () {

    // Your custom logic

};

Extending data grid

You can extend data grid and add new methods that are then callable on data grid.

$.datagrid.prototype.yourNewMethod = function () {

    // Your custom logic
    return this.pagination.page_index - 1;

};

You wont find fancy lifestyle graphics and marketing bravado here. Just cold... hard... code...

Code Well, Rock On!
Processing Payment...