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