Pagination using EmberJS and WebAPI

Rajnilari2015
Posted by in JavaScript category on for Beginner level | Points: 250 | Views : 4768 red flag
Rating: 5 out of 5  
 1 vote(s)

Ember.js is an open source JavaScript client-side framework for developing the web applications and uses the MVC(Model-View-Controller) architecture pattern. It uses handlebar template as view, route is used as model and controller manipulates the data in the model. In this article we will look into how to do pagination in EmberJS.


 Download source code for Pagination using EmberJS and WebAPI

Introduction

Ember.js is an open source JavaScript client-side framework for developing the web applications and uses the MVC(Model-View-Controller) architecture pattern. It uses handlebar template as view, route is used as model and controller manipulates the data in the model. In this article we will look into how to do pagination in EmberJS.

Creating the WebAPI project

Fire up Visual Studio and then File -> New -> Project -> Web -> Asp.Net Web Application.

The next screen that pops up display's a list of templates and we need to choose Web API template.

Also set the authentication mode to No Authentication

Clicking on the OK button creates the Web API project

Now let us first create the Employee model as under

namespace WebApplication1.Models
{
    public class Employee
    {
        public int EmployeeID { get; set; }
        public string EmployeeName { get; set; }
        public string EmailAddress { get; set; }
        public string PhoneNumber { get; set; }
    }
}

Our Employee model is a simple Employee Class which has some scalar properties like EmployeeID,EmployeeName,EmailAddress,PhoneNumber.

Now let us create the DBOperations class that will perform the pagination operation by using ADO.NET and Stored Procedures.

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;

namespace WebApplication1.Models
{
    public class DBOperations
    {
        /// <summary>
        /// Function: GetEmployees
        /// Get the Employee Records
        /// </summary>
        /// <returns></returns>
        public static List<Employee> GetEmployees()
        {
            List<Employee> lstEmployees = new List<Employee>();
            try
            {
                string ConnectionPath = ConfigurationManager.ConnectionStrings["DbConnection"].ToString();
                using (var sqlCon = new SqlConnection(ConnectionPath))
                {
                    using (SqlCommand cmd = new SqlCommand("usp_GetEmployees", sqlCon))
                    {                        
                        cmd.CommandType = CommandType.StoredProcedure;
                        sqlCon.Open();
                        SqlDataReader dr = cmd.ExecuteReader(CommandBehavior.CloseConnection);

                        while (dr.Read())
                        {
                            lstEmployees
                                .Add
                                (
                                    new Employee
                                    {
                                        EmployeeID = Convert.ToInt32(dr["EmployeeID"].ToString())
                                         ,
                                        EmployeeName = dr["EmployeeName"].ToString()
                                         ,
                                        EmailAddress = dr["EmailAddress"].ToString()
                                         ,
                                        PhoneNumber = dr["PhoneNumber"].ToString()
                                    }

                                );                           
                        }
                        dr.Close();
                    }
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            return lstEmployees;
        }

        /// <summary>
        /// Function: GetEmployeesForPagination
        /// Get the Employee Records for the range
        /// </summary>
        /// <param name="startIdx"></param>
        /// <param name="endIdx"></param>
        /// <returns></returns>
        public static List<Employee> GetEmployeesForPagination(int startIdx, int endIdx)
        {
            List<Employee> lstEmployees = new List<Employee>();
            try
            {
                string ConnectionPath = ConfigurationManager.ConnectionStrings["DbConnection"].ToString();
                using (var sqlCon = new SqlConnection(ConnectionPath))
                {
                    using (SqlCommand cmd = new SqlCommand("usp_GetEmployeesForPagination", sqlCon))
                    {
                        cmd.CommandType = CommandType.StoredProcedure;

                        cmd.Parameters.Add("@StartIndex", SqlDbType.Int).Value = startIdx;
                        cmd.Parameters.Add("@EndIndex", SqlDbType.Int).Value = endIdx;                       

                        sqlCon.Open();
                        SqlDataReader dr = cmd.ExecuteReader(CommandBehavior.CloseConnection);

                        while (dr.Read())
                        {
                            lstEmployees
                                .Add
                                (
                                    new Employee
                                    {
                                        EmployeeID = Convert.ToInt32(dr["EmployeeID"].ToString())
                                         ,
                                        EmployeeName = dr["EmployeeName"].ToString()
                                         ,
                                        EmailAddress = dr["EmailAddress"].ToString()
                                         ,
                                        PhoneNumber = dr["PhoneNumber"].ToString()
                                    }

                                );
                        }
                        dr.Close();
                    }
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            return lstEmployees;
        }        
    }
}

Now expand the Controllers folder and create EmployeeController.cs and add the following code

using System.Collections.Generic;
using System.Web.Http;
using WebApplication1.Models;

namespace WebApplication1.Controllers
{
    [RoutePrefix("Employee")]
    public class EmployeeController : ApiController
    {       
        [Route("EmployeeRecords")]
        [HttpGet]
        public List<Employee> GetEmployeeRecords()
        {
            return DBOperations.GetEmployees();            
        }

        [Route("EmployeeRecordsForPagination")]
        [HttpGet]
        public EmployeeRelatedInformation GetEmployeeRecordsForPagination(int startIdx,int endIdx)
        {
            EmployeeRelatedInformation eri = new EmployeeRelatedInformation();
            eri.EmployeeRecordCount = DBOperations.GetEmployees().Count;
            eri.Employees = DBOperations.GetEmployeesForPagination(startIdx, endIdx);
            return eri;
        }        
    }

    public class EmployeeRelatedInformation
    {
        public int EmployeeRecordCount { get; set; }
        public List<Employee> Employees { get; set; }
    }    
}

Basically, the GetEmployeeRecordsForPagination method accepts two parameters viz.startIdx and endIdx. These are the Start and End Index respectively. Suppose we have 20 records and we want to obtain records from 10 to 15. So, the value of startIdx will be 10 and endIdx will be 15. The EmployeeRelatedInformation class exposes two properties viz. a)EmployeeRecordCount - Total Records Count of teh Employees and b)List<Employee> Employees - The collection of Emloyee records within the specified range.

Now from the Nu-get package manager issue

Install-Package Microsoft.AspNet.WebApi.Cors

Once done, then open WebApiConfig.cs file and enable the CORS as under

The complete code is as under

using System.Web.Http;
using System.Web.Http.Cors;

namespace WebApplication1
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Web API configuration and services    
            config.EnableCors();
            config.EnableCors(new EnableCorsAttribute("*", "*", "*"));

            // Web API routes
            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
        }
    }
}

Run the application and click the API link in the browser to receive the below image

But we want to add some XML documentation comments to our API's. For doing so, let's open the Areas/HelpPage/App_Start/HelpPageConfig.cs

Open the HelpPageConfig.cs file. We will find that there is a HelpPageConfig static class whose purpose is to customize the Help Page. This class has a Register method where we need to uncomment the below.

config.SetDocumentationProvider(new XmlDocumentationProvider(HttpContext.Current.Server.MapPath("~/App_Data/XmlDocument.xml")));

Now, right click on the project and select Properties and then select the Build option. Under Output section, check XML documentation file. In the edit box, type App_Data/XmlDocument.xml.

Now add some comments to the web methods as under

    /// <summary>
    /// Provides employee related information
    /// </summary>
    [RoutePrefix("Employee")]
    public class EmployeeController : ApiController
    {
        /// <summary>       
        /// Get the Employee Records
        /// </summary>
        [Route("EmployeeRecords")]
        [HttpGet]
        public List<Employee> GetEmployeeRecords()
        {
            return DBOperations.GetEmployees();            
        }

        /// <summary>        
        /// Get the Employee Records for the range
        /// </summary>       
        [Route("EmployeeRecordsForPagination")]
        [HttpGet]
        public EmployeeRelatedInformation GetEmployeeRecordsForPagination(int startIdx,int endIdx)
        {
            EmployeeRelatedInformation eri = new EmployeeRelatedInformation();
            eri.EmployeeRecordCount = DBOperations.GetEmployees().Count;
            eri.Employees = DBOperations.GetEmployeesForPagination(startIdx, endIdx);
            return eri;
        }
     }

Now again run the application and click the API link in the browser to receive the updated information

Now we can test the API Services of the EmployeeController.cs through PostMan.

Design the Database Table and Stored Procedures

Next execute the below database script that will create both the table (say tblEmployees) and the stored procedures.

USE [YOURDATABASE]
GO
/****** Object:  Table [dbo].[tblEmployees]    Script Date: 8/24/2016 4:08:08 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[tblEmployees](
	[EmployeeID] [int] IDENTITY(1,1) NOT NULL,
	[EmployeeName] [varchar](50) NOT NULL,
	[EmailAddress] [varchar](50) NOT NULL,
	[PhoneNumber] [varchar](50) NOT NULL,
 CONSTRAINT [PK_tblEmployee] PRIMARY KEY CLUSTERED 
(
	[EmployeeID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO
SET ANSI_PADDING OFF
GO
/****** Object:  StoredProcedure [dbo].[usp_GetEmployees]    Script Date: 8/24/2016 4:08:08 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author:		RNA Team
-- Description:	Get employee records
-- exec [dbo].[usp_GetEmployees]
-- =============================================
CREATE PROCEDURE [dbo].[usp_GetEmployees] 
	-- Add the parameters for the stored procedure here	
AS
BEGIN
	-- SET NOCOUNT ON added to prevent extra result sets from
	-- interfering with SELECT statements.
	SET NOCOUNT ON;

    -- Insert statements for procedure here
	SELECT 
			EmployeeID
			,EmployeeName
			,EmailAddress
			,PhoneNumber
	FROM [dbo].[tblEmployees]
END
GO
/****** Object:  StoredProcedure [dbo].[usp_GetEmployeesForPagination]    Script Date: 8/24/2016 4:08:08 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author:		RNA Team
-- Description:	Select Employee Records within the Range
-- exec [dbo].[usp_GetEmployeesForPagination] 19,20
-- =============================================
CREATE PROCEDURE [dbo].[usp_GetEmployeesForPagination] 
	-- Add the parameters for the stored procedure here	
(
	@StartIndex INT
	,@EndIndex INT
)
AS
BEGIN
	-- SET NOCOUNT ON added to prevent extra result sets from
	-- interfering with SELECT statements.
	SET NOCOUNT ON;

    -- Insert statements for procedure here
	SELECT 
			EmployeeID
			,EmployeeName
			,EmailAddress
			,PhoneNumber
	FROM [dbo].[tblEmployees]
	WHERE EmployeeID BETWEEN @StartIndex AND @EndIndex
END
GO

As can be figure out that, in the usp_GetEmployeesForPagination we are selecting the records within the range

......
......
WHERE EmployeeID BETWEEN @StartIndex AND @EndIndex

We can populate the table with some random data by executing the below script

;WITH NumCte AS
(
	Select Rn=1 
	Union All
	Select Rn=Rn+1 From NumCte Where Rn < 50
)
INSERT INTO [dbo].[tblEmployees]
           ([EmployeeName]
           ,[EmailAddress]
           ,[PhoneNumber])
Select 
		EmployeeName = 'Name' + Cast(Rn As Varchar(10))
		,EmailAddress = 'Email' + Cast(Rn As Varchar(10)) + '@test.com'
		,PhoneNumber=ABS(CHECKSUM(NEWID()))		
From NumCte
Option(MaxRecursion 0)

This will insert 50 records to the [dbo].[tblEmployees] table

Implementing Pagination in EmberJS

Let us create a "index.html" and add the below

<!DOCTYPE html>
  <html>
     <head> 
        <script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/3.0.1/handlebars.min.js"></script>
        <script src="https://code.jquery.com/jquery-2.1.3.min.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/ember.js/1.10.0/ember.min.js"></script>
        <script src="http://builds.emberjs.com/tags/v1.10.0-beta.3/ember-template-compiler.js"></script>
        <script src="http://builds.emberjs.com/release/ember.debug.js"></script>
        <script src="http://builds.emberjs.com/beta/ember-data.js"></script>
     </head>
     <body>

        <script type="text/x-handlebars">
           {{outlet}}
        </script>


        <script type="text/x-handlebars" data-template-name="index">
        <h2>Pagination in EmberJS</h2>
         <button {{action "Previous"}} id="btnPrev"> Previous  </button>
         <button {{action "Next"}} id="btnNext"> Next  </button>

        <table border="1">   
        
              <tr>
              	  <th>EmployeeID</th>
                  <th>EmployeeName</th>
                  <th>EmailAddress</th>
                  <th>PhoneNumber</th>
              </tr>
              {{#each employee in App.employeeRecord}}
              <tr>
                  <td>{{employee.EmployeeID}}</td>
                  <td>{{employee.EmployeeName}}</td>
      			   <td>{{employee.EmailAddress}}</td>   
      			   <td>{{employee.PhoneNumber}}</td>             
              </tr>
              {{/each}}
        </table>  

        </script>

        <script type="text/javascript">
          var App = Ember.Application.create()

	        App.pageSize = 10; //number of records to be displayed
	     	App.startIndex=1; //start page index
	     	App.endIndex = App.pageSize; //end page index
	     	App.employeeRecord = null; //this will hold the employee records
	        App.employeeRecordCount = 0;  //stored the total Employee Records count        


		//the below code will execute on page load with the startIndex = 1 and endIndex = 10
		var uRL = 'http://localhost:22900/Employee/EmployeeRecordsForPagination?startIdx='+App.startIndex+'&endIdx='+App.endIndex;
                var empData = null;
                $.ajax({
                type: "get",
                url: uRL,
                cache: false,
                data: {},
                async:   false,
                success: function (data) {                                 
                    empData = data;
                },
                error: function (err) {               
                    alert(err);
                }
            }); 
                
          App.employeeRecordCount = empData.EmployeeRecordCount;
          App.employeeRecord = empData.Employees;
	  //---------end

          App.IndexRoute = Ember.Route.extend({

            //defining the action for the User click event
            actions: {
            
               Previous: function() {                 
                  App.startIndex=App.startIndex - App.pageSize ;
				          App.endIndex=App.endIndex - App.pageSize ;


        				      if(App.startIndex == 1){   

                      		document.getElementById("btnPrev").disabled = true;
                      		getEmployeeRecordsInRange(App.startIndex,App.endIndex); 

                		    } else { 

                      		document.getElementById("btnNext").disabled = false;
                      		getEmployeeRecordsInRange(App.startIndex,App.endIndex); }

                      } //end of Previous function
               ,             
            
                Next: function() { 
                  App.startIndex=App.startIndex + App.pageSize ;
				          App.endIndex=App.endIndex + App.pageSize ; 

				              if(App.endIndex === App.employeeRecordCount){

        			            document.getElementById("btnNext").disabled = true;
        			            getEmployeeRecordsInRange(App.startIndex,App.endIndex);

                			} else {
            			
                  			document.getElementById("btnPrev").disabled = false;
                  			getEmployeeRecordsInRange(App.startIndex,App.endIndex); }

                    } //end of Next function
              }//end action

         }); //end extend function


          //function name: getEmployeeRecordsInRange
          function getEmployeeRecordsInRange(startIDX,endIDX)
          {
          	
            var uRL = 'http://localhost:22900/Employee/EmployeeRecordsForPagination?startIdx='+startIDX+'&endIdx='+endIDX;                
            
            var empData = null;

                $.ajax({
                          type: "get",
                          url: uRL,
                          cache: false,
                          data: {},
                          async:   false,
                          success: function (data) {
                              empData = data; }
                          , error: function (err) {               
                              alert(err);}
                      }); 

            //Ember.set method is use to reflect the updated changes it in the handlebar template
            Ember.set(App,'employeeRecord',empData.Employees); 
          }	
        </script>
     </body>
  </html>

When the page loads, then the default startIndex and EndIndex values are set and the JQuery's "ajax" method is invoked for the WebAPI call. The values return from the API call is use for storing the total employee record count (in employeeRecordCount variable) and the employee records for the range in employeeRecord variable. This gets displayed through the Handlebars expressions by using the curly braces: {{}}. Here {{outlet}} is the placeholder that the router fills the appropriate template. The result is as under (on page load)

Previous method

This method will be triggered while the previous button is clicked. Here the start and end index is calculated based on the below formula.

App.startIndex=App.startIndex - App.pageSize ;
App.endIndex=App.endIndex - App.pageSize ; 

That means, whatever is the current page start and end index, we need to subtract the pagesize from that.

Next method

This method will be triggered while the next button is clicked. Here the start and end index is calculated based on the below formula.

App.startIndex=App.startIndex + App.pageSize ;
App.endIndex=App.endIndex + App.pageSize ;

That means, whatever is the current page start and end index, we need to add the pagesize to that.

Now click on the "Next" button till it reached the last record

Now click on the "Previous" button till it reached the first record

Reference

EmberJS

Conclusion

In this article we have learnt at the bare minimum

  1. Creating WebAPI
  2. Interacting with Ado.net
  3. How to write XML documentation comments to Web API's.
  4. Pagination concept in stored procedure
  5. Pagination implementation using EnberJS
  6. Display the record in EmberJS
  7. Use and importance of Ember.set method

Hope this will be useful. Thanks for reading. Zipped file attached.

Page copy protected against web site content infringement by Copyscape

About the Author

Rajnilari2015
Full Name: Niladri Biswas (RNA Team)
Member Level: Platinum
Member Status: Member,Microsoft_MVP,MVP
Member Since: 3/17/2015 2:41:06 AM
Country: India
-- Thanks & Regards, RNA Team


Login to vote for this post.

Comments or Responses

Login to post response

Comment using Facebook(Author doesn't get notification)