I'm trying to create a simple working example of using ng-grid with ASP.NET WebAPI. Thus, I started from the server-side paging example in the ng-grid examples page (http://angular-ui.github.io/ng-grid/); anyway, my grid always shows empty columns, even if when debugging I can confirm that data are received properly. Probably I'm just missing something in the grid setup, but all the samples I found look similar to mine. Could anyone help? Here is what I did:
Update #1: the suggested solution seems to work but only for the 1st page. Whenever I move to a new page or do any other operation requiring a refresh, the displayed data stay the same even if the server returned data change as expected. Also, from all the code samples I found it seems the correct way of setting data is just replacing the array member value rather than emptying and filling it again. I tried with apply as suggested in https://groups.google.com/forum/#!searchin/angular/nggrid/angular/vUIfHWt4s_4/oU_C9w8j-uMJ, but I get the same result.
Server side
Just create a new MVC4 app, update NuGet packages and add angular and ng-grid packages. My fake data model is represented by the Item class:
public sealed class Item
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public bool IsFemale { get; set; }
}
I also add a couple of models for dealing with paging, filtering and sorting various sets of data (I find easier to have a common paging base model -PagedFilter-, and a number of derived models):
public class PagedFilter
{
private int _nPageSize;
private int _nPageNumber;
public int PageSize
{
get { return _nPageSize; }
set
{
if (value < 1) throw new ArgumentOutOfRangeException("value");
_nPageSize = value;
}
}
public int PageNumber
{
get { return _nPageNumber; }
set
{
if (value < 1) throw new ArgumentOutOfRangeException("value");
_nPageNumber = value;
}
}
public int TotalItems { get; set; }
public int TotalPages
{
get { return (int)Math.Ceiling((double)(TotalItems / PageSize)); }
}
public PagedFilter()
{
_nPageSize = 20;
_nPageNumber = 1;
}
}
Here is the ItemFilter:
public class ItemFilter : PagedFilter
{
public List<string> SortFields { get; set; }
public List<string> SortDirections { get; set; }
public string Name { get; set; }
public int? MinAge { get; set; }
public int? MaxAge { get; set; }
}
Then I add an API controller for getting items:
public class ItemController : ApiController
{
// fake data
private readonly List<Item> _items;
public ItemController()
{
Random rnd = new Random();
_items = new List<Item>();
char c = 'a';
for (int i = 0; i < 1000; i++)
{
_items.Add(new Item
{
Id = i,
Age = rnd.Next(1, 100),
IsFemale = ((i & 1) == 0),
Name = String.Format(CultureInfo.InvariantCulture, "{0:00000}-{1}",
i, new string(c, 5))
});
if (++c > 'z') c = 'a';
}
}
public dynamic Get([FromUri] ItemFilter filter)
{
var items = _items.AsQueryable();
// filtering
if (!String.IsNullOrEmpty(filter.Name))
items = items.Where(i => i.Name.Contains(filter.Name));
if (filter.MinAge.HasValue)
items = items.Where(i => i.Age >= filter.MinAge.Value);
if (filter.MaxAge.HasValue)
items = items.Where(i => i.Age <= filter.MaxAge.Value);
// ...sorting (using Dynamic Linq) omitted for brevity...
// paging
int nTotalItems = items.Count();
items = items.Skip((filter.PageNumber - 1) * filter.PageSize)
.Take(filter.PageSize);
return new
{
totalItems = nTotalItems,
items = items.ToArray()
};
}
}
Client side
On the client side, my angular app is just a single controller modeled on the ng-grid sample: thus I directly add properties to $scope, even if in a real-world scenario I'd rather use a model (probably generated from a TypeScript class). HTML:
<div ng-app="MyApp" ng-controller="MainController">
<div ng-grid="gridOptions" style="height: 400px">
</div>
</div>
JS:
var app = angular.module('MyApp', ['ngGrid']);
app.controller('MainController', ['$scope', '$http', function ($scope, $http, $apply) {
$scope.items = [];
// filter
$scope.filterOptions = {
filterText: "",
useExternalFilter: true
};
// paging
$scope.totalServerItems = 0;
$scope.pagingOptions = {
pageSizes: [25, 50, 100],
pageSize: 25,
currentPage: 1
};
// sort
$scope.sortOptions = {
fields: ["name"],
directions: ["ASC"]
};
// grid
$scope.gridOptions = {
data: "items",
columnDefs: [
{ field: "name", displayName: "Name", pinnable: true },
{ field: "age", displayName: "Age", width: "60" },
{ field: "isFemale", displayName: "F", width: "40" }
],
enablePaging: true,
enablePinning: true,
pagingOptions: $scope.pagingOptions,
filterOptions: $scope.filterOptions,
keepLastSelected: true,
multiSelect: false,
showColumnMenu: true,
showFilter: true,
showGroupPanel: true,
showFooter: true,
sortInfo: $scope.sortOptions,
totalServerItems: "totalServerItems",
useExternalSorting: true,
i18n: "en"
};
$scope.refresh = function() {
setTimeout(function () {
var p = {
name: $scope.filterOptions.filterText,
pageNumber: $scope.pagingOptions.currentPage,
pageSize: $scope.pagingOptions.pageSize,
sortFields: $scope.sortOptions.fields,
sortDirections: $scope.sortOptions.directions
};
$http({
url: "/api/item",
method: "GET",
params: p
}).success(function(data, status, headers, config) {
$scope.totalServerItems = data.totalItems;
// SUGGESTION #1 -- empty and fill the array
/* $scope.items.length = 0;
angular.forEach(data.items, function (item) {
$scope.items.push(item);
});
*/
// https://groups.google.com/forum/#!searchin/angular/nggrid/angular/vUIfHWt4s_4/oU_C9w8j-uMJ
$scope.$apply(function () { $scope.items = data.items; });
if (!$scope.$$phase) {
$scope.$apply();
}
}).error(function(data, status, headers, config) {
alert(JSON.stringify(data));
});
}, 100);
};
// watches
$scope.$watch('pagingOptions', function (newVal, oldVal) {
if (newVal !== oldVal && newVal.currentPage !== oldVal.currentPage) {
$scope.refresh();
}
}, true);
$scope.$watch('filterOptions', function (newVal, oldVal) {
if (newVal !== oldVal) {
$scope.refresh();
}
}, true);
$scope.$watch('sortOptions', function (newVal, oldVal) {
if (newVal !== oldVal) {
$scope.refresh();
}
}, true);
$scope.refresh();
}]);
In my code, the success callback is called, and I can browse all the returned items in data.items. Yet, nothing is displayed in the grid. No error appears in the console.