HI WELCOME TO SIRIS

Server-side paging and sorting in React JS

Leave a Comment

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.

[Note: If you already visited my previous article and implemented the same, you can start from step - 7.]
Here In this article, I have used Visual Studio 2013 

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) > OK

Step-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.

In this example, I have used a table as below


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 code
  1. public ActionResult Index()
  2. {
  3. return View();
  4. }

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 
  1. @{
  2. ViewBag.Title = "Index";
  3. }
  4.  
  5. <h2>Show tabular data from database in React JS with server-side paging and sorting</h2>
  6.  
  7. @* HTML *@
  8. <div class="container" id="griddata">
  9. </div>
  10.  
  11. @* CSS *@
  12. <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" />
  13. <link href="//maxcdn.bootstrapcdn.com/font-awesome/4.1.0/css/font-awesome.min.css" rel="stylesheet" />
  14. @* Jquery *@
  15. <script src="https://code.jquery.com/jquery-2.2.0.min.js"></script>
  16. @* ReactJS library *@
  17. <script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.6/react.js"></script>
  18. <script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.6/react-dom.js"></script>
  19. @* JSX Parser *@
  20. <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>
  21. @* ReactJS component *@
  22.  
  23. <script type="text/babel">
  24. @* Here we will create React component *@
  25.  
  26. @* 1. Paging component *@
  27. var GridPager = React.createClass({
  28. render : function(){
  29. var li = [];
  30. var pageCount = this.props.Size;
  31. for(var i = 1; i <=pageCount; i++){
  32. if(this.props.currentPage == i){
  33. li.push(<li key={i} className="active"><a href="#">{i}</a></li>);
  34. }
  35. else{
  36. li.push(<li key={i} ><a href="#" onClick={this.props.onPageChanged.bind(null,i)}>{i}</a></li>);
  37. }
  38. }
  39. return (<ul className="pagination">{li}</ul>);
  40. }
  41. });
  42. @* 2. Table row component *@
  43. var EmployeeGridRow = React.createClass({
  44. render : function(){
  45. return (
  46. <tr>
  47. <td>{this.props.item.FirstName}</td>
  48. <td>{this.props.item.LastName}</td>
  49. <td>{this.props.item.EmailID}</td>
  50. <td>{this.props.item.Country}</td>
  51. <td>{this.props.item.City}</td>
  52. </tr>
  53. );
  54. }
  55. });
  56. @* 3. Table component *@
  57. var EmployeeGridTable = React.createClass({
  58. getInitialState : function(){
  59. return {
  60. Data : {
  61. List : [],
  62. totalPage : 0,
  63. sortColumnName : null,
  64. sortOrder : null,
  65. currentPage : 1,
  66. pageSize : 3
  67. }
  68. }
  69. },
  70. componentDidMount : function(){
  71. this.populateData();
  72. },
  73. @* function for populate data *@
  74. populateData: function(){
  75. var params = {
  76. pageSize : this.state.Data.pageSize,
  77. currentPage : this.state.Data.currentPage
  78. }
  79. if(this.state.Data.sortColumnName){
  80. params.sortColumnName = this.state.Data.sortColumnName;
  81. }
  82. if(this.state.Data.sortOrder){
  83. params.sortOrder = this.state.Data.sortOrder;
  84. }
  85.  
  86. $.ajax({
  87. url : this.props.dataUrl,
  88. type : 'GET',
  89. data : params,
  90. success : function(data){
  91. if(this.isMounted()){
  92. this.setState({
  93. Data : data
  94. });
  95. }
  96. }.bind(this),
  97. error: function(err){
  98. alert('Error');
  99. }.bind(this)
  100. });
  101. },
  102. @* function for pagination *@
  103. pageChanged:function(pageNumber,e){
  104. e.preventDefault();
  105. this.state.Data.currentPage = pageNumber;
  106. this.populateData();
  107. },
  108. @* function for sorting *@
  109. sortChanged : function(sortColumnName, order , e){
  110. e.preventDefault();
  111. this.state.Data.sortColumnName = sortColumnName;
  112. this.state.Data.currentPage = 1;
  113. this.state.Data.sortOrder = order.toString().toLowerCase() == 'asc' ? 'desc':'asc';
  114. this.populateData();
  115. },
  116. @* function for set sort icon on table header *@
  117. _sortClass : function(filterName){
  118. return "fa fa-fw " + ((filterName == this.state.Data.sortColumnName) ? ("fa-sort-" + this.state.Data.sortOrder) : "fa-sort");
  119. },
  120. @* render *@
  121. render : function(){
  122. var rows = [];
  123. this.state.Data.List.forEach(function(item){
  124. rows.push(<EmployeeGridRow key={item.EmployeeID} item={item}/>);
  125. });
  126. return (
  127. <div>
  128. <table className="table table-responsive table-bordered">
  129. <thead>
  130. <tr>
  131. <th onClick={this.sortChanged.bind(this,'FirstName',this.state.Data.sortOrder)}>First Name
  132. <i className={this._sortClass('FirstName')}></i></th>
  133. <th onClick={this.sortChanged.bind(this,'LastName',this.state.Data.sortOrder)}>
  134. Last Name
  135. <i className={this._sortClass('LastName')}></i></th>
  136. <th onClick={this.sortChanged.bind(this,'EmailID',this.state.Data.sortOrder)}>
  137. Email
  138. <i className={this._sortClass('EmailID')}></i>
  139. </th>
  140. <th onClick={this.sortChanged.bind(this,'Country',this.state.Data.sortOrder)}>
  141. Country
  142. <i className={this._sortClass('Country')}></i>
  143. </th>
  144. <th onClick={this.sortChanged.bind(this,'City',this.state.Data.sortOrder)}>
  145. City
  146. <i className={this._sortClass('City')}></i>
  147. </th>
  148. </tr>
  149. </thead>
  150. <tbody>{rows}</tbody>
  151. </table>
  152. <GridPager Size={this.state.Data.totalPage} onPageChanged={this.pageChanged} currentPage={this.state.Data.currentPage}/>
  153. </div>
  154. );
  155. }
  156. });
  157. ReactDOM.render(<EmployeeGridTable dataUrl="/home/getEmployeeList"/>, document.getElementById('griddata'));
  158. </script>
  159. <style>
  160. #griddata th
  161. {
  162. cursor: pointer;
  163. }
  164. .fa
  165. {
  166. float: right;
  167. margin-top: 4px;
  168. }
  169. </style>

If you see, here I have created three React components named GridPager (for generating paging links), EmployeeGridTable (for generating the table, where data will be displayed) and EmployeeGridRow (for generating table rows with data item).

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 packages
Go 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 code
  1. public JsonResult getEmployeeList(string sortColumnName = "FirstName", string sortOrder = "asc", int pageSize=3, int currentPage=1)
  2. {
  3. List<Employee> List = new List<Employee>();
  4. int totalPage = 0;
  5. int totalRecord = 0;
  6.  
  7. using (MyDatabaseEntities dc = new MyDatabaseEntities())
  8. {
  9. var emp = dc.Employees;
  10. totalRecord = emp.Count();
  11. if (pageSize > 0)
  12. {
  13. totalPage = totalRecord / pageSize + ((totalRecord % pageSize) > 0 ? 1 : 0);
  14. List = emp.OrderBy(sortColumnName + " " + sortOrder).Skip(pageSize * (currentPage - 1)).Take(pageSize).ToList();
  15. }
  16. else
  17. {
  18. List = emp.ToList();
  19. }
  20. }
  21.  
  22. return new JsonResult
  23. {
  24. //Data = new { List = List, totalPage = totalPage, sortColumnName = sortColumnName, sortOrder = sortOrder, currentPage = currentPage},
  25. Data = new { List = List, totalPage = totalPage, sortColumnName = sortColumnName, sortOrder = sortOrder, currentPage = currentPage, pageSize = pageSize },
  26. JsonRequestBehavior = JsonRequestBehavior.AllowGet
  27. };
  28. }
[Note: you can see the line 24 (yellow marked), I have updated this line on 19th feb 2016. Added a missing parameter pageSize]

Step-10: Run Application.

[Note: Here I have updated the code for add search box. You just need to update 2 steps code of this article, step-7 and step-9. Here I have marked updated lines with yellow background.] download
Updated step-7
  1. @{
  2. ViewBag.Title = "Index";
  3. }
  4.  
  5. <h2>Show tabular data from database in React JS with server-side paging and sorting</h2>
  6.  
  7. @* HTML *@
  8. <div class="container" id="griddata">
  9. </div>
  10.  
  11. @* CSS *@
  12. <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" />
  13. <link href="//maxcdn.bootstrapcdn.com/font-awesome/4.1.0/css/font-awesome.min.css" rel="stylesheet" />
  14. @* Jquery *@
  15. <script src="https://code.jquery.com/jquery-2.2.0.min.js"></script>
  16. @* ReactJS library *@
  17. <script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.6/react.js"></script>
  18. <script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.6/react-dom.js"></script>
  19. @* JSX Parser *@
  20. <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>
  21. @* ReactJS component *@
  22.  
  23. <script type="text/babel">
  24. @* Here we will create React component *@
  25. @* New : Search box component *@
  26. var SearchBox = React.createClass({
  27. handleChange : function(e){
  28. console.log('search');
  29. this.props.onSearchChanged(e.target.value);
  30. },
  31. render: function(){
  32. return (
  33. <div style={{marginBottom:15+'px',float:'right'}} ><input type="text" value={this.props.searchText} placeholder="search" onChange={this.handleChange}/></div>
  34. )
  35. }
  36. });
  37. @* 1. Paging component *@
  38. var GridPager = React.createClass({
  39. render : function(){
  40. var li = [];
  41. var pageCount = this.props.Size;
  42. for(var i = 1; i <=pageCount; i++){
  43. if(this.props.currentPage == i){
  44. li.push(<li key={i} className="active"><a href="#">{i}</a></li>);
  45. }
  46. else{
  47. li.push(<li key={i} ><a href="#" onClick={this.props.onPageChanged.bind(null,i)}>{i}</a></li>);
  48. }
  49. }
  50. return (<ul className="pagination">{li}</ul>);
  51. }
  52. });
  53. @* 2. Table row component *@
  54. var EmployeeGridRow = React.createClass({
  55. render : function(){
  56. return (
  57. <tr>
  58. <td>{this.props.item.FirstName}</td>
  59. <td>{this.props.item.LastName}</td>
  60. <td>{this.props.item.EmailID}</td>
  61. <td>{this.props.item.Country}</td>
  62. <td>{this.props.item.City}</td>
  63. </tr>
  64. );
  65. }
  66. });
  67. @* 3. Table component *@
  68. var EmployeeGridTable = React.createClass({
  69. getInitialState : function(){
  70. return {
  71. Data : {
  72. List : [],
  73. totalPage : 0,
  74. sortColumnName : null,
  75. sortOrder : null,
  76. currentPage : 1,
  77. pageSize : 3,
  78. searchText:''
  79. }
  80. }
  81. },
  82. componentDidMount : function(){
  83. this.populateData();
  84. },
  85. @* function for populate data *@
  86. populateData: function(){
  87. var params = {
  88. pageSize : this.state.Data.pageSize,
  89. currentPage : this.state.Data.currentPage,
  90. searchText : this.state.Data.searchText
  91. }
  92. if(this.state.Data.sortColumnName){
  93. params.sortColumnName = this.state.Data.sortColumnName;
  94. }
  95. if(this.state.Data.sortOrder){
  96. params.sortOrder = this.state.Data.sortOrder;
  97. }
  98.  
  99. $.ajax({
  100. url : this.props.dataUrl,
  101. type : 'GET',
  102. data : params,
  103. success : function(data){
  104. if(this.isMounted()){
  105. this.setState({
  106. Data : data
  107. });
  108. }
  109. }.bind(this),
  110. error: function(err){
  111. alert('Error');
  112. }.bind(this)
  113. });
  114. },
  115. @* function for pagination *@
  116. pageChanged:function(pageNumber,e){
  117. e.preventDefault();
  118. this.state.Data.currentPage = pageNumber;
  119. this.populateData();
  120. },
  121. @* function for sorting *@
  122. sortChanged : function(sortColumnName, order , e){
  123. e.preventDefault();
  124. this.state.Data.sortColumnName = sortColumnName;
  125. this.state.Data.currentPage = 1;
  126. this.state.Data.sortOrder = order.toString().toLowerCase() == 'asc' ? 'desc':'asc';
  127. this.populateData();
  128. },
  129. searchChange : function(value){
  130. var d = this.state.Data;
  131. d.searchText = value;
  132. this.setState({
  133. Data : d
  134. });
  135. this.populateData();
  136. },
  137. @* function for set sort icon on table header *@
  138. _sortClass : function(filterName){
  139. return "fa fa-fw " + ((filterName == this.state.Data.sortColumnName) ? ("fa-sort-" + this.state.Data.sortOrder) : "fa-sort");
  140. },
  141. @* render *@
  142. render : function(){
  143. var rows = [];
  144. this.state.Data.List.forEach(function(item){
  145. rows.push(<EmployeeGridRow key={item.EmployeeID} item={item}/>);
  146. });
  147. return (
  148. <div>
  149. <SearchBox onSearchChanged={this.searchChange} searchText={this.state.Data.searchText}/>
  150. <table className="table table-responsive table-bordered">
  151. <thead>
  152. <tr>
  153. <th onClick={this.sortChanged.bind(this,'FirstName',this.state.Data.sortOrder)}>First Name
  154. <i className={this._sortClass('FirstName')}></i></th>
  155. <th onClick={this.sortChanged.bind(this,'LastName',this.state.Data.sortOrder)}>
  156. Last Name
  157. <i className={this._sortClass('LastName')}></i></th>
  158. <th onClick={this.sortChanged.bind(this,'EmailID',this.state.Data.sortOrder)}>
  159. Email
  160. <i className={this._sortClass('EmailID')}></i>
  161. </th>
  162. <th onClick={this.sortChanged.bind(this,'Country',this.state.Data.sortOrder)}>
  163. Country
  164. <i className={this._sortClass('Country')}></i>
  165. </th>
  166. <th onClick={this.sortChanged.bind(this,'City',this.state.Data.sortOrder)}>
  167. City
  168. <i className={this._sortClass('City')}></i>
  169. </th>
  170. </tr>
  171. </thead>
  172. <tbody>{rows}</tbody>
  173. </table>
  174. <GridPager Size={this.state.Data.totalPage} onPageChanged={this.pageChanged} currentPage={this.state.Data.currentPage}/>
  175. </div>
  176. );
  177. }
  178. });
  179. ReactDOM.render(<EmployeeGridTable dataUrl="/home/getEmployeeList"/>, document.getElementById('griddata'));
  180. </script>
Updated step-9 
  1. public JsonResult getEmployeeList(string sortColumnName = "FirstName", string sortOrder = "asc", int pageSize = 3, int currentPage = 1, string searchText = "")
  2. {
  3. List<Employee> List = new List<Employee>();
  4. int totalPage = 0;
  5. int totalRecord = 0;
  6.  
  7. using (MyDatabaseEntities dc = new MyDatabaseEntities())
  8. {
  9. var emp = dc.Employees.Select(a => a);
  10. //Search
  11. if (!string.IsNullOrEmpty(searchText))
  12. {
  13. emp = emp.Where(a => a.FirstName.Contains(searchText) || a.LastName.Contains(searchText) || a.EmailID.Contains(searchText) || a.City.Contains(searchText) || a.Country.Contains(searchText));
  14. }
  15. totalRecord = emp.Count();
  16. if (pageSize > 0)
  17. {
  18. totalPage = totalRecord / pageSize + ((totalRecord % pageSize) > 0 ? 1 : 0);
  19. List = emp.OrderBy(sortColumnName + " " + sortOrder).Skip(pageSize * (currentPage - 1)).Take(pageSize).ToList();
  20. }
  21. else
  22. {
  23. List = emp.ToList();
  24. }
  25. }
  26.  
  27. return new JsonResult
  28. {
  29. //Data = new { List = List, totalPage = totalPage, sortColumnName = sortColumnName, sortOrder = sortOrder, currentPage = currentPage},
  30. Data = new { List = List, totalPage = totalPage, sortColumnName = sortColumnName, sortOrder = sortOrder, currentPage = currentPage, pageSize = pageSize, searchText = searchText },
  31. JsonRequestBehavior = JsonRequestBehavior.AllowGet
  32. };
  33. }

LIVE DEMO  DOWNLOAD

0 comments:

Post a Comment

Note: only a member of this blog may post a comment.