@trungdq88/react-datatable
v1.0.1
Published
DataTable component build with ReactJS, supports sort, search, filter, paging, custom cell value and more!
Downloads
1
Readme
react-datatable
DataTable component build with ReactJS, supports sort, search, filter, paging, custom cell value and more!
DEMO: http://trungdq88.github.io/react-datatable
How it looks like
Install
npm install --save @trungdq88/react-datatable
Usage
Be patience, it takes few steps to get make a minimum example but it totally worth!
1. Define a data source
Say I want to create a Restaurant list to DataTable, I first create a DataSource class which tells the DataTable where do I get the data from.
import { DataSource } from '@trungdq88/react-datatable';
// Don't forget to extends from DataSource
export default class RestaurantDataSource extends DataSource {
// This class will be initiate with a unique name and data
constructor(name, data) {
super(name);
this.meta = {
keyField: 'id',
searchFields: ['name'],
listFields: [
['Name', 'name'],
['Price', 'price'],
],
};
// Assign data to `items` property
this.items = data;
}
fetch() {
// For minimum example, we display all items in only 1 page (no pagination)
this.data = {
page: 1,
total: this.items.length,
entities: this.items,
perpage: this.items.length,
};
// Emit event
this.trigger('change');
}
}
2. Create a data source object
Create a data source object from RestaurantDataSource class with data
this.restaurantDataSource = new RestaurantDataSource('restaurants', [
{name: 'Japanese food'},
{name: 'Vietnamese food'},
{name: 'Thai food'},
]);
3. Create DataTable component:
In your render()
method:
<DataTable id="restaurants-table"
dataSource={this.restaurantDataSource} />
Your data table should be displayed right now.
Customize the table
DataSource
is the heart of DataTable. It can be customized a lot to serve various data
types.
The this.meta
property
Each DataSource
class has a property named meta
to describes the data,
it tells the DataTable
how to display data correctly.
Say, my data is an array of same schema objects:
[
{
id: 'VX129',
name: 'Vietnamese food',
price: '$$$$ (<100$)',
img: 'http://abc.com/some_image.png',
},
{
id: 'VX212',
name: 'Thai food',
price: '$$$ (<60$)',
img: 'http://abc.com/some_image.png',
}
]
This is the meta
object for that data:
this.meta = {
keyField: 'id',
searchFields: ['name'],
listFields: [
// Format: [{Heading text}, {Property name}]
['Name', 'name'],
['Price', 'price'],
],
};
DataTable display:
# | Name | Price
1 | Vietnamese food | $$$$ (<100$)
2 | Food food | $$$ (<60$)
Notice: the data must be an array, and all the objects in that array must share the same schema.
listFields
property and transform object
From above example, you might know that the listFields
property describes how the data
will be display on DataTable
(which property name displays in which column Header).
In some cases, we need more advanced method to customize the data displayed (for example display an image instead of regular text. That where we use the transform
object.
listFields: [
// Format: [{Heading text}, {Property name}]
['Image', {
// Field to be transformed
field: 'img',
// Receive `img` property in the first param
// Returns {string} or `ReactElement` to be displayed
transform: function (img) {
return <img src={img} />;
}
}],
['Name', 'name'],
['Price', 'price'],
],
The transform
function also receive the context (this
variable) of the parent data
object, that means you can reference to other property inside that transform object.
In the example bellow, I added a link (using id
property) to "Name" column:
listFields: [
['Image', {
field: 'img',
transform: function (img) {
return <img src={img} />;
}
}],
['Name', {
field: 'name',
transform: function (name) {
return <a href={'http://example.com/restaurant/detail/' + this.id}>{name}</a>;
}
}],
],
The this.fetch
method.
As you can see in RestaurantDataSource
, the fetch method only assign data to this.data
and trigger change
event. In fact, the fetch
methods accept various parameters. See this in the DataSource
class source code.
/**
* Start to fetch data (via API or whatever)
* Please implement this method to get data from any API endpoint
* or from a static array. This method should assign the `this.data`
* variable in the right schema which later will be get from `this.get()`
* method.
*
* Don't forget to call `this.trigger('change')` when the data is loaded
*
* @page {number} requested page number
* @search {string} keyword entered in search box
* @sortProperty {string} property of the column that need to be sorted
* @sortOrderDesc {boolean} equals `true` if sort in Desc order, `false` = Asc
* @perpage {number} number of items to displayed in one page
*/
fetch(page, search, sortProperty, sortOrderDesc, filter, perpage) {
console.error('Not implemented!');
}
So basically, you can create a data source that get data every where. Check this example on get data from Github issue API
fetch(page, search, sortProperty, sortOrderDesc, filter, perpage) {
const apiEndPoint = 'https://api.github.com/repos/facebook/react/issues';
// Build URL
// 1. Pagination
let query = '&page=' + page;
if (perpage) query += '&per_page=' + perpage;
// 2. Search
// Sad, GitHub API does not provide search
// if (search) query += '&query=' + search;
// 3. Sort
if (sortProperty && sortOrderDesc !== null) {
query += '&sort=' + sortProperty +
'&direction=' + (sortOrderDesc ? 'desc' : 'asc');
}
// 4. Filter // Sad, GitHub API does not provide filter
//if (filter && Object.keys(filter).length > 0) {
// query += Object.keys(filter).map((property) => {
// return '&' + property + '=' + filter[property];
// });
//}
// Send ajax request to URL
api.get(apiEndPoint + '?' + query)
.done((response) => {
// Set data
this.data = {
page: page,
total: undefined,
entities: response,
perpage: perpage || this.DEFAULT_PER_PAGE,
search: search,
sortProperty: sortProperty,
sortOrderDesc: sortOrderDesc,
};
// Emit event
this.trigger('change');
}).fail(() => {
this.trigger('failed');
});
More example on how to use DataSource
please see in examples/src
Search
Searchable fields is setted in meta
object with searchFields {array({string}))
property.
this.meta = {
keyField: 'id',
// Array of property name that will be used for search
searchFields: ['name'],
listFields: [
...
]
In order to display a search box to the top of the table, add searchable
property to
<DataTable>
element.
<DataTable id="restaurants-table"
dataSource={this.restaurantDataSource}
searchable />
When user click search button, the fetch
method will be called with search
parameter contains the keyword entered in the search box.
Sort
By default, all the property is sortable. If <DataTable>
has sortable
property, it
will display a sort icon right next to the column header, clicking to the sort icon will
trigger the fetch
method with sortProperty
and sortOrderDesc
param.
<DataTable id="restaurants-table"
dataSource={this.restaurantDataSource}
searchable
sortable />
If you want to disable sorting for a speficic column, add a 'no-sort' string to the 3-rd
element in listFields
item:
['Image', {
field: 'img',
transform: function (img) {
return <img src={img} />;
},
}, 'no-sort'],
You can use true
instead of no-sort
string, as long as the value is not undefined
.
if (listField[2] === undefined) {
// Display sort icon
} else {
// Hide sort icon
}
Filter
You can define the filter value for a specific column by adding a 4-th element in listFields
item:
listFields: [
['Image', {
field: 'img',
transform: function (img) {
return <img src={img} />;
}
}],
['Name', {
field: 'name',
transform: function (name) {
return <a href={'http://example.com/restaurant/detail/' + this.id}>{name}</a>;
}
}],
['Price', 'price', undefined, [
{id: '$$ (<$30)', label: '$$ (<$30)'},
{id: '$$$ (<$60)', label: '$$$ (<$60)'},
{id: '$$$$ (<$100)', label: '$$$$ (<$100)'},
{id: '$$$$$ (>$100)', label: '$$$$$ (>$100)'},
]]
]
Filter setting is an array of objects, which must follow this schema:
{
id: '', // Id value of the filter
label: '', // Label to display in DataTable
}
Handle filter in fetch
method: fetch method will receive a filter
object
{
propertyName1: propertyValue1,
propertyName2: propertyValue2,
...
}
See an example on filtering data in fetch
method:
fetch(page, search, sortProperty, sortOrderDesc, filter, perpage) {
let data = this.items;
// 1. Filter
if (filter && Object.keys(filter).length > 0) {
const filterFunc = function (item) {
return item[this] === filter[this]
};
for (const property in filter) {
if (property) {
data = data.filter(filterFunc.bind(property));
}
}
}
...
Extra columns
In some cases, you might want to add extra columns, for example add a "Edit", "Remove"
button to the last column. Create a extraColumns
array to describe the column you
want to add and then call dataSource.setExtraColumns
to add the column.
this.restaurantDataSource = new RestaurantDataSource(
'restaurant', fakeData.restaurants);
const onOrderRestaurant = (restaurant) => {
alert('Order placed for ' + restaurant.name);
}
const extraColumns = [
['Action', {
'field': '',
transform: function () {
return (
<button onClick={onOrderRestaurant.bind(self, this)}
className="btn btn-primary btn-sm">Order this restaurant</button>
);
},
}, 'no-sort'],
];
this.restaurantDataSource.setExtraColumns(extraColumns);
Basically, DataSource
will concat the extraColumns
with listFields
and return
to DataTable, therefore all the rules applied for listFields
is the same with
extraColumns
.
Development & Build
Build the demo page:
npm install
npm run build
Start dev server for development
npm start
No tests available yet.