Introduction
This is the 3rd article of the series "React.js with asp.net MVC application".In the previous post, I have shown you Displaying tabular data from database in react js Today I am going to show you server-side paging and sorting using React.js, ASP.NET MVC and entity framework.
In the previous article, we fetched all the data from a server at once which can be a performance issue fetching a large amount of data from server side at once. To resolve this performance issue, here in this article I will show you how to implement server-side paging and sorting in React JS.
Here We will fetch a list of employee data (JSON data) from a database via ajax and then we will create React components for displaying the employee data list in the tabular format with server-side paging and sorting.
Just follow the following steps in order to implement Datatables server-side paging, sorting and filtering in React JS.
Step - 1: Create New Project.
Go to File > New > Project > ASP.NET Web Application (under web) > Entry Application Name > Click OK > Select Empty template > Checked MVC (under "Add folders and core references for" option) > OKStep-2: Add a Database.
Go to Solution Explorer > Right Click on App_Data folder > Add > New item > Select SQL Server Database Under Data > Enter Database name > Add.Step-3: Create a table for store data.
Open Database > Right Click on Table > Add New Table > Add Columns > Save > Enter table name > Ok.Employee Table
Step-4: Add Entity Data Model.
Go to Solution Explorer > Right Click on Project name form Solution Explorer > Add > New item > Select ADO.net Entity Data Model under data > Enter model name > Add.A popup window will come (Entity Data Model Wizard) > Select Generate from database > Next >
Chose your data connection > select your database > next > Select tables > enter Model Namespace > Finish.
Step-5: Create an MVC Controller.
Go to Solution Explorer > Right Click on Controllers folder form Solution Explorer > Add > Controller > Enter Controller name > Select Templete "empty MVC Controller"> Add.Here I have created a controller named "HomeController"
Step-6: Add new action into your controller for getting the view, where we will implement our ReactJS component.
Here I have added "Index" Action into "Home" Controller. Please write this following codepublic ActionResult Index()
{
return View();
}
Step-7: Add view for your Action and design.
Right Click on Action Method (here right click on Index action) > Add View... > Enter View Name > Select View Engine (Razor) > Add.HTML Code
@{
ViewBag.Title = "Index";
}
<h2>Show tabular data from database in React JS with server-side paging and sorting</h2>
@* HTML *@
<div class="container" id="griddata">
</div>
@* CSS *@
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" />
<link href="//maxcdn.bootstrapcdn.com/font-awesome/4.1.0/css/font-awesome.min.css" rel="stylesheet" />
@* Jquery *@
<script src="https://code.jquery.com/jquery-2.2.0.min.js"></script>
@* ReactJS library *@
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.6/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.6/react-dom.js"></script>
@* JSX Parser *@
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>
@* ReactJS component *@
<script type="text/babel">
@* Here we will create React component *@
@* 1. Paging component *@
var GridPager = React.createClass({
render : function(){
var li = [];
var pageCount = this.props.Size;
for(var i = 1; i <=pageCount; i++){
if(this.props.currentPage == i){
li.push(<li key={i} className="active"><a href="#">{i}</a></li>);
}
else{
li.push(<li key={i} ><a href="#" onClick={this.props.onPageChanged.bind(null,i)}>{i}</a></li>);
}
}
return (<ul className="pagination">{li}</ul>);
}
});
@* 2. Table row component *@
var EmployeeGridRow = React.createClass({
render : function(){
return (
<tr>
<td>{this.props.item.FirstName}</td>
<td>{this.props.item.LastName}</td>
<td>{this.props.item.EmailID}</td>
<td>{this.props.item.Country}</td>
<td>{this.props.item.City}</td>
</tr>
);
}
});
@* 3. Table component *@
var EmployeeGridTable = React.createClass({
getInitialState : function(){
return {
Data : {
List : [],
totalPage : 0,
sortColumnName : null,
sortOrder : null,
currentPage : 1,
pageSize : 3
}
}
},
componentDidMount : function(){
this.populateData();
},
@* function for populate data *@
populateData: function(){
var params = {
pageSize : this.state.Data.pageSize,
currentPage : this.state.Data.currentPage
}
if(this.state.Data.sortColumnName){
params.sortColumnName = this.state.Data.sortColumnName;
}
if(this.state.Data.sortOrder){
params.sortOrder = this.state.Data.sortOrder;
}
$.ajax({
url : this.props.dataUrl,
type : 'GET',
data : params,
success : function(data){
if(this.isMounted()){
this.setState({
Data : data
});
}
}.bind(this),
error: function(err){
alert('Error');
}.bind(this)
});
},
@* function for pagination *@
pageChanged:function(pageNumber,e){
e.preventDefault();
this.state.Data.currentPage = pageNumber;
this.populateData();
},
@* function for sorting *@
sortChanged : function(sortColumnName, order , e){
e.preventDefault();
this.state.Data.sortColumnName = sortColumnName;
this.state.Data.currentPage = 1;
this.state.Data.sortOrder = order.toString().toLowerCase() == 'asc' ? 'desc':'asc';
this.populateData();
},
@* function for set sort icon on table header *@
_sortClass : function(filterName){
return "fa fa-fw " + ((filterName == this.state.Data.sortColumnName) ? ("fa-sort-" + this.state.Data.sortOrder) : "fa-sort");
},
@* render *@
render : function(){
var rows = [];
this.state.Data.List.forEach(function(item){
rows.push(<EmployeeGridRow key={item.EmployeeID} item={item}/>);
});
return (
<div>
<table className="table table-responsive table-bordered">
<thead>
<tr>
<th onClick={this.sortChanged.bind(this,'FirstName',this.state.Data.sortOrder)}>First Name
<i className={this._sortClass('FirstName')}></i></th>
<th onClick={this.sortChanged.bind(this,'LastName',this.state.Data.sortOrder)}>
Last Name
<i className={this._sortClass('LastName')}></i></th>
<th onClick={this.sortChanged.bind(this,'EmailID',this.state.Data.sortOrder)}>
Email
<i className={this._sortClass('EmailID')}></i>
</th>
<th onClick={this.sortChanged.bind(this,'Country',this.state.Data.sortOrder)}>
Country
<i className={this._sortClass('Country')}></i>
</th>
<th onClick={this.sortChanged.bind(this,'City',this.state.Data.sortOrder)}>
City
<i className={this._sortClass('City')}></i>
</th>
</tr>
</thead>
<tbody>{rows}</tbody>
</table>
<GridPager Size={this.state.Data.totalPage} onPageChanged={this.pageChanged} currentPage={this.state.Data.currentPage}/>
</div>
);
}
});
ReactDOM.render(<EmployeeGridTable dataUrl="/home/getEmployeeList"/>, document.getElementById('griddata'));
</script>
<style>
#griddata th
{
cursor: pointer;
}
.fa
{
float: right;
margin-top: 4px;
}
</style>
Also, I have added functions populateData (for fetching data from the server), pageChanged (paging functionality), sortChanged (for sorting event) and _sortClass (for adding sort icon on table header column).
Step-8: Add reference of System.Linq.Dynamic
Here I have added System.Linq.Dynamic reference from NuGet packagesGo to Solution Explorer > Right click on References > Manage NuGet packages > Search with "System.Linq.Dynamic" > Install.
Step-9: Add another action to your controller for return data(employee) list as JSON Data
Here I have used "GetEmployeeData" Action for fetch data. Please write this following codepublic JsonResult getEmployeeList(string sortColumnName = "FirstName", string sortOrder = "asc", int pageSize=3, int currentPage=1)
{
List<Employee> List = new List<Employee>();
int totalPage = 0;
int totalRecord = 0;
using (MyDatabaseEntities dc = new MyDatabaseEntities())
{
var emp = dc.Employees;
totalRecord = emp.Count();
if (pageSize > 0)
{
totalPage = totalRecord / pageSize + ((totalRecord % pageSize) > 0 ? 1 : 0);
List = emp.OrderBy(sortColumnName + " " + sortOrder).Skip(pageSize * (currentPage - 1)).Take(pageSize).ToList();
}
else
{
List = emp.ToList();
}
}
return new JsonResult
{
//Data = new { List = List, totalPage = totalPage, sortColumnName = sortColumnName, sortOrder = sortOrder, currentPage = currentPage},
Data = new { List = List, totalPage = totalPage, sortColumnName = sortColumnName, sortOrder = sortOrder, currentPage = currentPage, pageSize = pageSize },
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}
Step-10: Run Application.
@{
ViewBag.Title = "Index";
}
<h2>Show tabular data from database in React JS with server-side paging and sorting</h2>
@* HTML *@
<div class="container" id="griddata">
</div>
@* CSS *@
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" />
<link href="//maxcdn.bootstrapcdn.com/font-awesome/4.1.0/css/font-awesome.min.css" rel="stylesheet" />
@* Jquery *@
<script src="https://code.jquery.com/jquery-2.2.0.min.js"></script>
@* ReactJS library *@
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.6/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.6/react-dom.js"></script>
@* JSX Parser *@
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>
@* ReactJS component *@
<script type="text/babel">
@* Here we will create React component *@
@* New : Search box component *@
var SearchBox = React.createClass({
handleChange : function(e){
console.log('search');
this.props.onSearchChanged(e.target.value);
},
render: function(){
return (
<div style={{marginBottom:15+'px',float:'right'}} ><input type="text" value={this.props.searchText} placeholder="search" onChange={this.handleChange}/></div>
)
}
});
@* 1. Paging component *@
var GridPager = React.createClass({
render : function(){
var li = [];
var pageCount = this.props.Size;
for(var i = 1; i <=pageCount; i++){
if(this.props.currentPage == i){
li.push(<li key={i} className="active"><a href="#">{i}</a></li>);
}
else{
li.push(<li key={i} ><a href="#" onClick={this.props.onPageChanged.bind(null,i)}>{i}</a></li>);
}
}
return (<ul className="pagination">{li}</ul>);
}
});
@* 2. Table row component *@
var EmployeeGridRow = React.createClass({
render : function(){
return (
<tr>
<td>{this.props.item.FirstName}</td>
<td>{this.props.item.LastName}</td>
<td>{this.props.item.EmailID}</td>
<td>{this.props.item.Country}</td>
<td>{this.props.item.City}</td>
</tr>
);
}
});
@* 3. Table component *@
var EmployeeGridTable = React.createClass({
getInitialState : function(){
return {
Data : {
List : [],
totalPage : 0,
sortColumnName : null,
sortOrder : null,
currentPage : 1,
pageSize : 3,
searchText:''
}
}
},
componentDidMount : function(){
this.populateData();
},
@* function for populate data *@
populateData: function(){
var params = {
pageSize : this.state.Data.pageSize,
currentPage : this.state.Data.currentPage,
searchText : this.state.Data.searchText
}
if(this.state.Data.sortColumnName){
params.sortColumnName = this.state.Data.sortColumnName;
}
if(this.state.Data.sortOrder){
params.sortOrder = this.state.Data.sortOrder;
}
$.ajax({
url : this.props.dataUrl,
type : 'GET',
data : params,
success : function(data){
if(this.isMounted()){
this.setState({
Data : data
});
}
}.bind(this),
error: function(err){
alert('Error');
}.bind(this)
});
},
@* function for pagination *@
pageChanged:function(pageNumber,e){
e.preventDefault();
this.state.Data.currentPage = pageNumber;
this.populateData();
},
@* function for sorting *@
sortChanged : function(sortColumnName, order , e){
e.preventDefault();
this.state.Data.sortColumnName = sortColumnName;
this.state.Data.currentPage = 1;
this.state.Data.sortOrder = order.toString().toLowerCase() == 'asc' ? 'desc':'asc';
this.populateData();
},
searchChange : function(value){
var d = this.state.Data;
d.searchText = value;
this.setState({
Data : d
});
this.populateData();
},
@* function for set sort icon on table header *@
_sortClass : function(filterName){
return "fa fa-fw " + ((filterName == this.state.Data.sortColumnName) ? ("fa-sort-" + this.state.Data.sortOrder) : "fa-sort");
},
@* render *@
render : function(){
var rows = [];
this.state.Data.List.forEach(function(item){
rows.push(<EmployeeGridRow key={item.EmployeeID} item={item}/>);
});
return (
<div>
<SearchBox onSearchChanged={this.searchChange} searchText={this.state.Data.searchText}/>
<table className="table table-responsive table-bordered">
<thead>
<tr>
<th onClick={this.sortChanged.bind(this,'FirstName',this.state.Data.sortOrder)}>First Name
<i className={this._sortClass('FirstName')}></i></th>
<th onClick={this.sortChanged.bind(this,'LastName',this.state.Data.sortOrder)}>
Last Name
<i className={this._sortClass('LastName')}></i></th>
<th onClick={this.sortChanged.bind(this,'EmailID',this.state.Data.sortOrder)}>
Email
<i className={this._sortClass('EmailID')}></i>
</th>
<th onClick={this.sortChanged.bind(this,'Country',this.state.Data.sortOrder)}>
Country
<i className={this._sortClass('Country')}></i>
</th>
<th onClick={this.sortChanged.bind(this,'City',this.state.Data.sortOrder)}>
City
<i className={this._sortClass('City')}></i>
</th>
</tr>
</thead>
<tbody>{rows}</tbody>
</table>
<GridPager Size={this.state.Data.totalPage} onPageChanged={this.pageChanged} currentPage={this.state.Data.currentPage}/>
</div>
);
}
});
ReactDOM.render(<EmployeeGridTable dataUrl="/home/getEmployeeList"/>, document.getElementById('griddata'));
</script>
Updated step-9
public JsonResult getEmployeeList(string sortColumnName = "FirstName", string sortOrder = "asc", int pageSize = 3, int currentPage = 1, string searchText = "")
{
List<Employee> List = new List<Employee>();
int totalPage = 0;
int totalRecord = 0;
using (MyDatabaseEntities dc = new MyDatabaseEntities())
{
var emp = dc.Employees.Select(a => a);
//Search
if (!string.IsNullOrEmpty(searchText))
{
emp = emp.Where(a => a.FirstName.Contains(searchText) || a.LastName.Contains(searchText) || a.EmailID.Contains(searchText) || a.City.Contains(searchText) || a.Country.Contains(searchText));
}
totalRecord = emp.Count();
if (pageSize > 0)
{
totalPage = totalRecord / pageSize + ((totalRecord % pageSize) > 0 ? 1 : 0);
List = emp.OrderBy(sortColumnName + " " + sortOrder).Skip(pageSize * (currentPage - 1)).Take(pageSize).ToList();
}
else
{
List = emp.ToList();
}
}
return new JsonResult
{
//Data = new { List = List, totalPage = totalPage, sortColumnName = sortColumnName, sortOrder = sortOrder, currentPage = currentPage},
Data = new { List = List, totalPage = totalPage, sortColumnName = sortColumnName, sortOrder = sortOrder, currentPage = currentPage, pageSize = pageSize, searchText = searchText },
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}
Live Demo Download