Creating angularJS grid for MVC application

Working with data input and output grids is one of most commonly executed tasks in applications. It does not matter if you are developing web application, desktop application or mobile application, it some point you are going to have to develop interface for grid. In this post I am going to show a prototype that builds a grid using angularJS and MVC.

Prototype

Like my previous discussion I am going to start by showing live prototype.

Auctions

{{auction.Name}}

{{auction.Category}}

Bids placed: {{auction.NumberOfBids}}
Current Bid: {{auction.CurrentBid | currency}}
Auction ends in

{{auction.remainingTime|durationview}}

Name Category
{{item.Category}}

The HTML code used to build this prototype is as follows.

 <div ng-controller="AuctionItemsController">
  <div class="row">
   <div class="col-xs-5" ng-repeat="auction in auctions">
   <div class="panel panel-info">
     <div class="panel-body">
     <h3>{{auction.Name}}</h3>
     <h4>{{auction.Category}}</h4>
     <div><strong>Bids placed:</strong> {{auction.NumberOfBids}}</div>
     <div><strong>Current Bid:</strong> {{auction.CurrentBid | currency}}</div>
     <div class="input-group">
     <input class="form-control" ng-model="auction.myBid" />
      <span class="input-group-btn"><button class="btn btn-primary" 
         ng-click="placeBid(auction.Id, auction.myBid)">Place Bid</button></span>
     </div>
     <div><strong>Auction ends in</strong></div>
     <div><h3 class="text-primary"> {{auction.remainingTime|durationview}}</h3></div>
    </div>
   </div>
  </div>
 </div>
 <div class="row">
 <div class="col-xs-6">
 <table class="table table-hover">
  <thead>
   <tr>
    <th>Name</th>
    <th></th>
    <th>Category</th>
   </tr>
  </thead>
  <tbody>
  <tr ng-repeat="item in auctions">
   <td><input ng-model="item.Name" /></td>
   <td>{{item.Category}}</td>
   <td>
    <select ng-model="item.Category" ng-options="category.Name as category.Name for category in categories">
    </select>
   </td>
  </tr>
 </tbody>
 </table>
 <button class="btn btn-primary" ng-click="updateAuctionItems()">Update</button>
</div>
</div>
</div>

You can update the items in the grid shown in bottom half. It presents you will a text box bound to Name property of the auction item in the mode and a dropdown list that has its selection bound to Category property of the auction item. You will notice that as you make changes to any of these editable properties in grid, the same values will update in upper blocks displaying these entries as well. This is where power of angularJS show that how everything fits in transparently.

angularJS controller for MVC grid

Following is updated version of controller that has been used for this prototype.

        ByteBlocksApp.controller(" auctionitemscontroller", ['$scope', '$http' , '$log' , 
      '$timeout' , 'datetime' , function ($scope, $http, $log, $timeout, datetime) {
         $scope.apicontroller=PortalSettings.baseUrl + "/api/auction" ;
         $scope.listsyncinprogress=false;
         $scope.xsrftokenfield="__RequestVerificationToken" ;
         $scope.categories=[];
         var getauctionitems=function () {
         $http({ method 'GET' , url $scope.apicontroller + '/GetAuctionItems' , params { maxitems 5 } }).
         success(function (data, status, headers, config) {
         processauctionitems(data);
         $scope.auctions=data;
         }).
         error(function (data, status, headers, config) { />/TODO: log this error
        });
    };
    var getCategories = function () {
        $http({ method: 'GET', url: $scope.apiController + '/GetCategories', params: { maxItems: 5 } }).
            success(function (data, status, headers, config) {
                $scope.categories = data;
            }).
            error(function (data, status, headers, config) {
                //TODO: log this error
            });
    };
    var tick = function () {
        $scope.currentTime = moment();
        processAuctionItems($scope.auctions);
        $timeout(tick, 1000);
    }
    var findAuctionItem = function (id) {
        var foundItem = null;
        angular.forEach($scope.auctions, function (item) {
            if (item.Id == id) {
                foundItem = item;
                return false;
            }
        });
        return foundItem;
    };
    var updateAuctionItem = function (data) {
        var item = findAuctionItem(data.Id);
        item.CurrentBid = data.CurrentBid;
        item.NumberOfBids = data.NumberOfBids;
        item.LastBidDateTime = data.LastBidDateTime;
        item.myBid = data.CurrentBid + data.BidIncrement;
    };
    var processAuctionItems = function (data) {
        angular.forEach(data, function (item) {
            item.remainingTime = datetime.getRemainigTime(item.AuctionEndDateTime);
            item.myBid = item.CurrentBid + item.BidIncrement;
        });
    }
    $scope.placeBid = function (id, bidAmount) {
        $http({ method: 'POST', url: $scope.apiController + '/PostBid', 
                 data: { Id: id, BidAmount: bidAmount } }).
            success(function (data, status, headers, config) {
                switch (status) {
                    case 200:
                        updateAuctionItem(data.Data);
                        break;
                    default:
                        //TODO: Log this error
                        break;
                }
            }).
            error(function (data, status, headers, config) {
            });
    }
    $scope.updateAuctionItems = function () {
        var itemsData = angular.toJson($scope.auctions);
        $http({ method: 'POST', url: $scope.apiController + '/PostAuctionItems', data: itemsData }).
            success(function (data, status, headers, config) {
                switch (status) {
                    case 200:
                        $log.info("auction items updated");
                        break;
                    default:
                        break;
                }
            }).
            error(function (data, status, headers, config) {
                alert(data);
            });
    };
    $scope.currentTime = moment();
    getCategories();
    getAuctionItems();
    $timeout(tick, 1000);
}]);

There are few noticable differences from the controller that was used in previous prototype samples.

  • New behavior getCategories has been added to get collection of category objects from server
  • New behavior updateAuctionItems has been added that posts the updates from grid to server using $http service

Asp.Net Web Api

After the changes have been made in the grid, there is nothing special you have to do create the dataset to be sent in POST request's body. As you are making the changes in the input elements in grid, angularJS framework has already updated the model representing the collection of items. Notice the following line in updateAuctionItems function.

 var itemsData = angular.toJson($scope.auctions);

When this JSON formatted data reaches MVC framework on server side, it deserializes the data into the model expected by signature of web api action. The following code snippet shows how the server side code looks that that receives POST request from client.

[System.Web.Http.HttpPost]
public ActionStatus PostAuctionItems(IEnumerable<AuctionItem> items)
{
    var status = new ActionStatus() { StatusCode = 200 };
    foreach (var item in items)
    {
        _dataProvider.UpdateAuctionItem(item);
    }
    return status;
}

As you can see that I did not have to do anything special on server side for MVC to create the collection object from JSON data that was posted to it.

This is all I had to do to create a grid using angularJS.

comments powered by Disqus

Blog Tags