Custom Deferred Grid Using MVC Web API And AngularJS

In this article we are going to see how to create a custom deferred grid in MVC using Web API and AngularJS. We will be creating a custom UI for the grid, and using web API and AngularJS $http services we will fetch the data from the database. Normally we use ADO.NET Entity data model as the model class when we work with a Web API, right?

Instead of using an Entity data model, here we are going to use our normal SqlDataAdapter and SqlConnection and stored procedure to get the data from our database. We use Virtual Repeat in AngularJS for loading the data in UI, so that the data will be loaded whenever there is a user action that is scrolling (Virtual Scrolling). So that in the view port we will load only few items first. Now shall we go and see this in detail? I hope you will like this.
 
Download the source code

You can always download the source code here:

Background

We have so many plugins available to show the data in a grid format, don't we? if you want to know few of them, you can find them here. Now what if you need to show the data in a grid format without using any additional plugins? What if you need to load the data to that grid dynamically, that is whenever user scrolls the grid? If you could not find the answer for these questions, here in this post I am going to share an option. I hope you will enjoy reading.

Create a MVC application

Click File, New, Project and then select MVC application. Before going to start the coding part, make sure that AngularJS is installed. You can see all the items mentioned above from NuGet. Right click on your project name and select Manage NuGet packages.

Angular JS NuGet Package Visual Studio

AngularJS NuGet Package Visual Studio

Once you have installed, please make sure that all the items are loaded in your scripts folder.

Using the code

I hope everything is set now, then it is time to start our coding. First we will create a controller action and a view. Below is the code snippet of our controller.

  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Web;  
  5. using System.Web.Mvc;  
  6. namespace Custom_Deffered_Grid_Using_MVC_Web_API_And_Angular_JS.Controllers  
  7. {  
  8.     public class DefaultController: Controller  
  9.     {  
  10.         // GET: Default  
  11.         public ActionResult Index()  
  12.         {  
  13.             return View();  
  14.         }  
  15.     }  
  16. }  
Here Default/em> is my controller name. Now create a view for this action, and load the needed references.
  1. @{  
  2. ViewBag.Title = "Index";  
  3. }  
  4. <h2>Index</h2>  
  5. <link href="~/Content/angular-material.css" rel="stylesheet" />  
  6. <script src="~/scripts/angular.min.js"></script>  
  7. <script src="~/scripts/angular-route.min.js"></script>  
  8. <script src="~/scripts/angular-aria.min.js"></script>  
  9. <script src="~/scripts/angular-animate.min.js"></script>  
  10. <script src="~/scripts/angular-messages.min.js"></script>  
  11. <script src="~/scripts/angular-material.js"></script>  
  12. <script src="~/scripts/svg-assets-cache.js"></script>  
  13. <script src="~/scripts/Default/Default.js"></script>  
You can get these files from the source code attached with this article. And the file Default.js is the additional file where we are requested to do our additional scripts. So far the basic implementation of our view is done. Now we will create a Web API and additional model class to fetch the data from the database. Are you ready? 

Below is my Web API controller.

  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Net;  
  5. using System.Net.Http;  
  6. using System.Web.Http;  
  7. using Custom_Deffered_Grid_Using_MVC_Web_API_And_Angular_JS.Models;  
  8. namespace Custom_Deffered_Grid_Using_MVC_Web_API_And_Angular_JS.Controllers  
  9. {  
  10.     public class DataAPIController : ApiController  
  11.     {  
  12.       DataModel dm = new DataModel();  
  13.       public string getData(int id)  
  14.         {  
  15.           var d = dm.fetchData(id);  
  16.           return d;  
  17.         }  
  18.     }  
  19. }  
Have you noticed that I have included using Custom_Deffered_Grid_Using_MVC_Web_API_And_Angular_JS.Models; in the section? This is to ensure that we can use the model classes whatever we have created so far. In this case DataModel is our model class and we are creating an instance for the same.
  1. DataModel dm = new DataModel();  
The controller action getData accepting the parameter id, right? This is actually the page offset value which we are passing from the client side. Now we will create our model class, you can find the code snippets below for that.

  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Web;  
  5. using System.Data;  
  6. using System.Data.Sql;  
  7. using System.Data.SqlClient;  
  8. using System.Configuration;  
  9. namespace Custom_Deffered_Grid_Using_MVC_Web_API_And_Angular_JS.Models  
  10. {  
  11.     public class DataModel  
  12.     {  
  13.     public string fetchData(int pageOffset)  
  14.     {  
  15.         string connection = ConfigurationManager.ConnectionStrings["TrialsDBEntities"].ConnectionString;  
  16.         using (SqlConnection cn = new SqlConnection(connection))  
  17.         {  
  18.         SqlCommand cmd = new SqlCommand("usp_Get_SalesOrderDetailPage", cn);  
  19.         cmd.Parameters.Add("@pageoffset", SqlDbType.Int).Value = pageOffset;  
  20.         cmd.CommandType = CommandType.StoredProcedure;  
  21.         try  
  22.             {  
  23.             DataTable dt = new DataTable();  
  24.             SqlDataAdapter da = new SqlDataAdapter(cmd);  
  25.             cn.Open();  
  26.             da.Fill(dt);  
  27.             return GetJson(dt);  
  28.             }  
  29.         catch (Exception)  
  30.         {  
  31.         throw;  
  32.         }  
  33.       }  
  34.     }  
  35.  }  
  36. }
As I said before, instead of using an entity model we use our normal sql connections and sql data adapter to load the data. Before going to use this function, please make sure that you have added the below references.
  1. using System.Data;  
  2. using System.Data.Sql;  
  3. using System.Data.SqlClient;  
  4. using System.Configuration;
Now coming back to the fetchData function, we use the connection string TrialsDBEntities from the web config file. So it is mandatory that you must have a connection string with that name in your web config file. Once that is done, we call the stored procedure usp_Get_SalesOrderDetailPage and fill the data using SqlDataAdapter. 

Another thing to be notified here is we are passing that DataTable to a function called GetJson. So you must have the definition for that too.

  1. public string GetJson(DataTable dt)  
  2. {  
  3.  try  
  4.  {  
  5.    if (dt == null)  
  6.     {  
  7.       throw new ArgumentNullException("dt");  
  8.     }  
  9.    System.Web.Script.Serialization.JavaScriptSerializer serializer = new System.Web.Script.Serialization.JavaScriptSerializer();  
  10.    List<Dictionary<stringobject>> rows = new List<Dictionary<stringobject>>();  
  11.    Dictionary<stringobject> row = null;  
  12.    foreach (DataRow dr in dt.Rows)  
  13.    {  
  14.     row = new Dictionary<stringobject>();  
  15.     foreach (DataColumn col in dt.Columns)  
  16.      {  
  17.        row.Add(col.ColumnName.Trim(), dr[col]);  
  18.      }  
  19.      rows.Add(row);  
  20.    }  
  21. return serializer.Serialize(rows);  
  22. }  
  23. catch (Exception)  
  24. {  
  25. throw;  
  26. }  
What this function does is, it converts the data table to a JSON format. So far, the coding related to Web API is done, now it is time to create a database, table, and a stored procedure. 

Create a database

The following query can be used to create a database in your SQL Server.

  1. USE [master]  
  2. GO  
  3. /****** Object: Database [TrialsDB] Script Date: 25-Feb-16 12:34:32 PM ******/  
  4. CREATE DATABASE [TrialsDB]  
  5. CONTAINMENT = NONE  
  6. ON PRIMARY  
  7. NAME = N'TrialsDB', FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\DATA\TrialsDB.mdf' , SIZE = 3072KB , MAXSIZE = UNLIMITED, FILEGROWTH = 1024KB )  
  8. LOG ON  
  9. NAME = N'TrialsDB_log', FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\DATA\TrialsDB_log.ldf' , SIZE = 1024KB , MAXSIZE = 2048GB , FILEGROWTH = 10%)  
  10. GO  
  11. ALTER DATABASE [TrialsDB] SET COMPATIBILITY_LEVEL = 110  
  12. GO  
  13. IF (1 = FULLTEXTSERVICEPROPERTY('IsFullTextInstalled'))  
  14. begin  
  15. EXEC [TrialsDB].[dbo].[sp_fulltext_database] @action = 'enable'  
  16. end  
  17. GO  
  18. ALTER DATABASE [TrialsDB] SET ANSI_NULL_DEFAULT OFF  
  19. GO  
  20. ALTER DATABASE [TrialsDB] SET ANSI_NULLS OFF  
  21. GO  
  22. ALTER DATABASE [TrialsDB] SET ANSI_PADDING OFF  
  23. GO  
  24. ALTER DATABASE [TrialsDB] SET ANSI_WARNINGS OFF  
  25. GO  
  26. ALTER DATABASE [TrialsDB] SET ARITHABORT OFF  
  27. GO  
  28. ALTER DATABASE [TrialsDB] SET AUTO_CLOSE OFF  
  29. GO  
  30. ALTER DATABASE [TrialsDB] SET AUTO_CREATE_STATISTICS ON  
  31. GO  
  32. ALTER DATABASE [TrialsDB] SET AUTO_SHRINK OFF  
  33. GO  
  34. ALTER DATABASE [TrialsDB] SET AUTO_UPDATE_STATISTICS ON  
  35. GO  
  36. ALTER DATABASE [TrialsDB] SET CURSOR_CLOSE_ON_COMMIT OFF  
  37. GO  
  38. ALTER DATABASE [TrialsDB] SET CURSOR_DEFAULT GLOBAL  
  39. GO  
  40. ALTER DATABASE [TrialsDB] SET CONCAT_NULL_YIELDS_NULL OFF  
  41. GO  
  42. ALTER DATABASE [TrialsDB] SET NUMERIC_ROUNDABORT OFF  
  43. GO  
  44. ALTER DATABASE [TrialsDB] SET QUOTED_IDENTIFIER OFF  
  45. GO  
  46. ALTER DATABASE [TrialsDB] SET RECURSIVE_TRIGGERS OFF  
  47. GO  
  48. ALTER DATABASE [TrialsDB] SET DISABLE_BROKER  
  49. GO  
  50. ALTER DATABASE [TrialsDB] SET AUTO_UPDATE_STATISTICS_ASYNC OFF  
  51. GO  
  52. ALTER DATABASE [TrialsDB] SET DATE_CORRELATION_OPTIMIZATION OFF  
  53. GO  
  54. ALTER DATABASE [TrialsDB] SET TRUSTWORTHY OFF  
  55. GO  
  56. ALTER DATABASE [TrialsDB] SET ALLOW_SNAPSHOT_ISOLATION OFF  
  57. GO  
  58. ALTER DATABASE [TrialsDB] SET PARAMETERIZATION SIMPLE  
  59. GO  
  60. ALTER DATABASE [TrialsDB] SET READ_COMMITTED_SNAPSHOT OFF  
  61. GO  
  62. ALTER DATABASE [TrialsDB] SET HONOR_BROKER_PRIORITY OFF  
  63. GO  
  64. ALTER DATABASE [TrialsDB] SET RECOVERY FULL  
  65. GO  
  66. ALTER DATABASE [TrialsDB] SET MULTI_USER  
  67. GO  
  68. ALTER DATABASE [TrialsDB] SET PAGE_VERIFY CHECKSUM  
  69. GO  
  70. ALTER DATABASE [TrialsDB] SET DB_CHAINING OFF  
  71. GO  
  72. ALTER DATABASE [TrialsDB] SET FILESTREAM( NON_TRANSACTED_ACCESS = OFF )  
  73. GO  
  74. ALTER DATABASE [TrialsDB] SET TARGET_RECOVERY_TIME = 0 SECONDS  
  75. GO  
  76. ALTER DATABASE [TrialsDB] SET READ_WRITE  
  77. GO 

Now we will create a table. 

Create table in database

Below is the query to create table in database.

  1. USE [TrialsDB]  
  2. GO  
  3. /****** Object: Table [dbo].[SalesOrderDetail] Script Date: 25-Feb-16 12:35:45 PM ******/  
  4. SET ANSI_NULLS ON  
  5. GO  
  6. SET QUOTED_IDENTIFIER ON  
  7. GO  
  8. CREATE TABLE [dbo].[SalesOrderDetail](  
  9. [SalesOrderID] [intNOT NULL,  
  10. [SalesOrderDetailID] [int] IDENTITY(1,1) NOT NULL,  
  11. [CarrierTrackingNumber] [nvarchar](25) NULL,  
  12. [OrderQty] [smallintNOT NULL,  
  13. [ProductID] [intNOT NULL,  
  14. [SpecialOfferID] [intNOT NULL,  
  15. [UnitPrice] [money] NOT NULL,  
  16. [UnitPriceDiscount] [money] NOT NULL,  
  17. [LineTotal] AS (isnull(([UnitPrice]*((1.0)-[UnitPriceDiscount]))*[OrderQty],(0.0))),  
  18. [rowguid] [uniqueidentifier] ROWGUIDCOL NOT NULL,  
  19. [ModifiedDate] [datetime] NOT NULL,  
  20. CONSTRAINT [PK_SalesOrderDetail_SalesOrderID_SalesOrderDetailID] PRIMARY KEY CLUSTERED  
  21. (  
  22. [SalesOrderID] ASC,  
  23. [SalesOrderDetailID] ASC  
  24. )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ONON [PRIMARY]  
  25. ON [PRIMARY]  
  26. GO  

Can we insert some data to the table now?

Insert data to table

To insert the data, I will attach a database script file along with the download file, you can either run that or insert some data using the following query. By the way if you would like to know how to generate scripts with data in SQL Server, you can check here.

  1. USE [TrialsDB]  
  2. GO  
  3. INSERT INTO [dbo].[SalesOrderDetail]  
  4. ([SalesOrderID]  
  5. ,[CarrierTrackingNumber]  
  6. ,[OrderQty]  
  7. ,[ProductID]  
  8. ,[SpecialOfferID]  
  9. ,[UnitPrice]  
  10. ,[UnitPriceDiscount]  
  11. ,[rowguid]  
  12. ,[ModifiedDate])  
  13. VALUES  
  14. (<SalesOrderID, int,>  
  15. ,<CarrierTrackingNumber, nvarchar(25),>  
  16. ,<OrderQty, smallint,>  
  17. ,<ProductID, int,>  
  18. ,<SpecialOfferID, int,>  
  19. ,<UnitPrice, money,>  
  20. ,<UnitPriceDiscount, money,>  
  21. ,<rowguid, uniqueidentifier,>  
  22. ,<ModifiedDate, datetime,>)  
  23. GO

Along with this, we can create a new stored procedure which will fetch the data. The following is the query to create the stored procedure.

  1. USE [TrialsDB]  
  2. GO  
  3. /****** Object: StoredProcedure [dbo].[usp_Get_SalesOrderDetailPage] Script Date: 25-Feb-16 12:53:07 PM ******/  
  4. SET ANSI_NULLS ON  
  5. GO  
  6. SET QUOTED_IDENTIFIER ON  
  7. GO  
  8. -- =============================================  
  9. -- Author: <Author,Sibeesh Venu>  
  10. -- Create date: <Create Date, 18-Feb-2016>  
  11. -- Description: <Description,To fetch SalesOrderDetail Page Wise>  
  12. -- =============================================  
  13. ALTER PROCEDURE [dbo].[usp_Get_SalesOrderDetailPage] @pageOffset int=0 AS BEGIN -- SET NOCOUNT ON added to prevent extra result sets from  
  14. -- interfering with SELECT statements.  
  15. SET NOCOUNT ON;  
  16. WITH CTE_Sales(SlNo, SalesOrderID,SalesOrderDetailID,CarrierTrackingNumber,OrderQty,ProductID,UnitPrice,ModifiedDate) AS  
  17. SELECT ROW_NUMBER() over (  
  18. ORDER BY ModifiedDate DESCAS SlNo,  
  19. SalesOrderID,  
  20. SalesOrderDetailID,  
  21. CarrierTrackingNumber,  
  22. OrderQty,  
  23. ProductID,  
  24. UnitPrice,  
  25. ModifiedDate  
  26. FROM dbo.SalesOrderDetail)  
  27. SELECT *  
  28. FROM CTE_Sales  
  29. WHERE SlNo>=@pageOffset  
  30. AND SlNo<@pageOffset+10 END  
  31. --[usp_Get_SalesOrderDetailPage] 4  
Here we are using Common Table Expressions in SQL Server. If you are new to CTE, you can always visit"Common Table Expression Example" for some more information regarding that. It seems the database is ready with the data now. Then we can go back to our view. We will change our view as follows with the custom styles.
  1. @{  
  2. ViewBag.Title = "Index";  
  3. }  
  4. <h2>Index</h2>  
  5. <link href="~/Content/angular-material.css" rel="stylesheet" />  
  6. <style>  
  7. .virtualRepeatdemoDeferredLoading #vertical-container {  
  8. padding: 10px;  
  9. border: 1px solid #ccc;  
  10. border-radius: 5px;  
  11. box-shadow: 1px 10px 10px 1px #ccc;  
  12. background-color: #fff;  
  13. width: 40%;  
  14. height: 390px;  
  15. margin: 20px;  
  16. }  
  17. .virtualRepeatdemoDeferredLoading .repeated-item {  
  18. border-bottom: 1px solid #ddd;  
  19. box-sizing: border-box;  
  20. height: 40px;  
  21. padding: 10px;  
  22. border: 1px solid #ccc;  
  23. border-radius: 5px;  
  24. box-shadow: 1px 10px 10px 1px #ccc;  
  25. background-color: #fff;  
  26. width: 90%;  
  27. height: 120px;  
  28. margin: 20px;  
  29. color: #aaa;  
  30. font-size: 12px;  
  31. line-height: 20px;  
  32. }  
  33. .virtualRepeatdemoDeferredLoading md-content {  
  34. margin: 16px;  
  35. }  
  36. .virtualRepeatdemoDeferredLoading md-virtual-repeat-container {  
  37. border: solid 1px grey;  
  38. }  
  39. .virtualRepeatdemoDeferredLoading .md-virtual-repeat-container .md-virtual-repeat-offsetter div {  
  40. padding-left: 16px;  
  41. }  
  42. #introduction {  
  43. border-bottom: 1px solid #ddd;  
  44. box-sizing: border-box;  
  45. height: 40px;  
  46. padding: 10px;  
  47. border: 1px solid #ccc;  
  48. border-radius: 5px;  
  49. box-shadow: 1px 10px 10px 1px #ccc;  
  50. background-color: #fff;  
  51. width: 98%;  
  52. height: 70px;  
  53. color: #aaa;  
  54. font-size: 12px;  
  55. line-height: 25px;  
  56. }  
  57. </style>  
  58. <div ng-controller="AppCtrl as ctrl" ng-cloak="" class="virtualRepeatdemoDeferredLoading" ng-app="MyApp">  
  59. <md-content layout="column">  
  60. <div id="introduction">  
  61. <p>  
  62. Please scroll the Grid to load the data from database. This is a simple demo of deffered or virtual data loading in Angular JS.  
  63. We created this application MVC with Web API to fetch the data. I hope you enjoyed the demo. Please visit again <img src="http://sibeeshpassion.com/wp-includes/images/smilies/simple-smile.png" alt=":)" class="wp-smiley" style="height: 1em; max-height: 1em;">  
  64. </p>  
  65. </div>  
  66. <md-virtual-repeat-container id="vertical-container">  
  67. <div md-virtual-repeat="item in ctrl.dynamicItems" md-on-demand="" class="repeated-item" flex="">  
  68. <div> <b>SlNo:</b> {{item.SlNo}}, <b>SalesOrderID:</b> {{item.SalesOrderID}}</div>  
  69. <div> <b>SalesOrderDetailID:</b> {{item.SalesOrderDetailID}}, <b>CarrierTrackingNumber:</b> {{item.CarrierTrackingNumber}}</div>  
  70. <div> <b>OrderQty:</b> {{item.OrderQty}}, <b>ProductID:</b> {{item.ProductID}}</div>  
  71. <div> <b>UnitPrice:</b> {{item.UnitPrice}}</div>  
  72. </div>  
  73. </md-virtual-repeat-container>  
  74. </md-content>  
  75. </div>  
  76. <script src="~/scripts/angular.min.js"></script>  
  77. <script src="~/scripts/angular-route.min.js"></script>  
  78. <script src="~/scripts/angular-aria.min.js"></script>  
  79. <script src="~/scripts/angular-animate.min.js"></script>  
  80. <script src="~/scripts/angular-messages.min.js"></script>  
  81. <script src="~/scripts/angular-material.js"></script>  
  82. <script src="~/scripts/svg-assets-cache.js"></script>  
  83. <script src="~/scripts/Default/Default.js"></script>   

As you can see from the above code, our AngularJS controller is ng-controller=”AppCtrl as ctrl” and the AngularJS app is ng-app=”MyApp”. We use md-virtual-repeat as a repeater control, so that it can be used to loop through the object item in ctrl.dynamicItems. Now it is time to create our AngularJS scripts. Shall we?

We can create our Angular App and Controller as follows.

  1. (function () {  
  2. 'use strict';  
  3. angular  
  4. .module('MyApp', ['ngMaterial''ngMessages''material.svgAssetsCache'])  
  5. .controller('AppCtrl', function ($http, $timeout) {  
  6. });  
  7. })();
Now in the controller we will add a function with some predefined items as follows.
  1. var DynamicItems = function () {  
  2. this.loadedPages = {};  
  3. this.numItems = 0;  
  4. this.PAGE_SIZE = 10;  
  5. this.fetchNumItems_();  
  6. };  
Here loadedPages is the data collection which is keyed by the page number (Our parameter id in the controller). And numItems is the total number of items. PAGE_SIZE is the number of items to be fetched from each requests.

Now we will create a function to calculate the length of the records.

  1. DynamicItems.prototype.getLength = function () {  
  2. return this.numItems;  
  3. };  
This numItems can be set in the below function.
  1. DynamicItems.prototype.fetchNumItems_ = function () {  
  2. $timeout(angular.noop, 300).then(angular.bind(this, function () {  
  3. this.numItems = 1000;  
  4. }));  
  5. };  
Here we are setting the numItems as 1000 for demo purposes, you can always get the count from database and assign it here with a $http request as we load the data from database here, you are yet to see that, no worries.

Below is the function to get the item by index.

  1. DynamicItems.prototype.getItemAtIndex = function (index) {  
  2. var pageNumber = Math.floor(index / this.PAGE_SIZE);  
  3. var page = this.loadedPages[pageNumber];  
  4. if (page)  
  5.  {  
  6.    return page[index % this.PAGE_SIZE];  
  7.  } else if (page !== null)  
  8.  {  
  9.    this.fetchPage_(pageNumber);  
  10.  }  
  11. };  
Here is the main part to load the data from database using a $http service in AngularJS.
  1. DynamicItems.prototype.fetchPage_ = function (pageNumber) {  
  2. this.loadedPages[pageNumber] = null;  
  3. $timeout(angular.noop, 300).then(angular.bind(this, function () {  
  4. var thisObj = this;  
  5. this.loadedPages[pageNumber] = [];  
  6. var pageOffset = pageNumber * this.PAGE_SIZE;  
  7. var myData;  
  8. var url = '';  
  9. url = 'api/DataAPI/' + pageOffset;  
  10. $http({  
  11. method: 'GET',  
  12. url: url,  
  13. }).then(function successCallback(response) {  
  14. // this callback will be called asynchronously  
  15. // when the response is available  
  16. myData = JSON.parse(response.data);  
  17. pushLoadPages(thisObj, myData)  
  18. }, function errorCallback(response) {  
  19. console.log('Oops! Something went wrong while fetching the data. Status Code: ' + response.status + ' Status statusText: ' + response.statusText);  
  20. // called asynchronously if an error occurs  
  21. // or server returns response with an error status.  
  22. });  
  23. }));  
  24. };   

As you can see call to our Web API ( url = ‘api/DataAPI/’ + pageOffset;) from $http service, the callback functionSuccessCallback will get the data from database as a response. Once we get the response, we will pass the data to a function pushLoadPages to push the data items to the loadedPages. Cool right? Below is the code snippets for that function.

  1. function pushLoadPages(thisObj, servData)   
  2. {  
  3. if (servData != undefined)  
  4. {  
  5.     for (var i = 0; i < servData.length; i++)  
  6.     {  
  7.         thisObj.loadedPages[pageNumber].push(servData[i]);  
  8.     }  
  9. }  
  10. }  
Here is the complete code for our AngularJS. 

AngularJS Complete Code

  1. (function () {  
  2. 'use strict';  
  3. angular  
  4. .module('MyApp', ['ngMaterial''ngMessages''material.svgAssetsCache'])  
  5. .controller('AppCtrl', function ($http, $timeout) {  
  6. var DynamicItems = function () {  
  7. this.loadedPages = {};  
  8. this.numItems = 0;  
  9. this.PAGE_SIZE = 10;  
  10. this.fetchNumItems_();  
  11. };  
  12. DynamicItems.prototype.getItemAtIndex = function (index) {  
  13. var pageNumber = Math.floor(index / this.PAGE_SIZE);  
  14. var page = this.loadedPages[pageNumber];  
  15. if (page) {  
  16. return page[index % this.PAGE_SIZE];  
  17. else if (page !== null) {  
  18. this.fetchPage_(pageNumber);  
  19. }  
  20. };  
  21. DynamicItems.prototype.getLength = function () {  
  22. return this.numItems;  
  23. };  
  24. DynamicItems.prototype.fetchPage_ = function (pageNumber) {  
  25. this.loadedPages[pageNumber] = null;  
  26. $timeout(angular.noop, 300).then(angular.bind(this, function () {  
  27. var thisObj = this;  
  28. this.loadedPages[pageNumber] = [];  
  29. var pageOffset = pageNumber * this.PAGE_SIZE;  
  30. var myData;  
  31. var url = '';  
  32. url = 'api/DataAPI/' + pageOffset;  
  33. $http({  
  34. method: 'GET',  
  35. url: url,  
  36. }).then(function successCallback(response) {  
  37. // this callback will be called asynchronously  
  38. // when the response is available  
  39. myData = JSON.parse(response.data);  
  40. pushLoadPages(thisObj, myData)  
  41. }, function errorCallback(response) {  
  42. console.log('Oops! Something went wrong while fetching the data. Status Code: ' + response.status + ' Status statusText: ' + response.statusText);  
  43. // called asynchronously if an error occurs  
  44. // or server returns response with an error status.  
  45. });  
  46. function pushLoadPages(thisObj, servData) {  
  47. if (servData != undefined) {  
  48. for (var i = 0; i < servData.length; i++) {  
  49. thisObj.loadedPages[pageNumber].push(servData[i]);  
  50. }  
  51. }  
  52. }  
  53. }));  
  54. };  
  55. DynamicItems.prototype.fetchNumItems_ = function () {  
  56. $timeout(angular.noop, 300).then(angular.bind(this, function () {  
  57. this.numItems = 1000;  
  58. }));  
  59. };  
  60. this.dynamicItems = new DynamicItems();  
  61. });  
  62. })();    
Now it is time to see the output. 

Output

Custom Deffered Grid Using MVC Web API And Angular JS Output

Custom Deferred Grid Using MVC Web API And AngularJS Output

Custom Deffered Grid Using MVC Web API And Angular JS Deferred Output

Custom Deferred Grid Using MVC Web API And AngularJS Deferred Output

Have a happy coding.

Reference

  • AngularJS Materials
  • Conclusion

    Did I miss anything that you may think is needed? Did you try Web API yet? Have you ever wanted to do this requirement? Did you find this post useful? I hope you liked this article. Please share with me your valuable suggestions and feedback.

    Your turn. What do you think?

    A blog isn’t a blog without comments, but do try to stay on topic. If you have a question unrelated to this post, you’re better off posting it on C# Corner, Code Project, Stack Overflow, ASP.NET Forum instead of commenting here. Tweet or email me a link to your question there and I’ll definitely try to help if I can.

    Please see this article in my blog  here.
     
    Read more articles on ASP.NET:


    Similar Articles