服务器端分页+过滤+使用WebAPI对ng-grid进行排序

Naf*_*tis 24 asp.net-web-api angularjs ng-grid

我正在尝试创建一个使用ng-grid和ASP.NET WebAPI的简单工作示例.因此,我从ng-grid示例页面(http://angular-ui.github.io/ng-grid/)中的服务器端分页示例开始; 无论如何,我的网格总是显示空列,即使在调试时我可以确认数据是否正确接收.可能我只是在网格设置中遗漏了一些东西,但我发现的所有样本看起来都与我的相似.有人可以帮忙吗?这是我做的:

更新#1:建议的解决方案似乎有效但仅适用于第1页.每当我移动到新页面或执行需要刷新的任何其他操作时,即使服务器返回数据按预期更改,显示的数据也保持不变.此外,从我发现的所有代码示例中,似乎正确的设置数据的方法只是替换数组成员值而不是清空并再次填充它.我按照https://groups.google.com/forum/#!searchin/angular/nggrid/angular/vUIfHWt4s_4/oU_C9w8j-uMJ的建议尝试了申请,但我得到的结果相同.


服务器端

只需创建一个新的MVC4应用程序,更新NuGet包并添加angular和ng-grid包.我的假数据模型由Item类表示:

public sealed class Item
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
    public bool IsFemale { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

我还添加了几个模型来处理分页,过滤和排序各种数据集(我发现更容易有一个共同的分页基础模型-PagedFilter-,以及一些派生模型):

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这是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; }
}
Run Code Online (Sandbox Code Playgroud)

然后我添加一个API控制器来获取项目:

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()
                   };
    }
}
Run Code Online (Sandbox Code Playgroud)

客户端

在客户端,我的角度应用程序只是在ng-grid示例上建模的单个控制器:因此我直接向$ scope添加属性,即使在真实场景中我宁愿使用模型(可能是从TypeScript类).HTML:

<div ng-app="MyApp" ng-controller="MainController">
    <div ng-grid="gridOptions" style="height: 400px">
    </div>
</div>
Run Code Online (Sandbox Code Playgroud)

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();
}]);
Run Code Online (Sandbox Code Playgroud)

在我的代码中,调用了成功回调,我可以浏览data.items中的所有返回项.然而,网格中没有显示任何内容.控制台中不会显示错误.

Naf*_*tis 13

经过一番实验,我想我找到了正确的代码.这篇关于$ apply的帖子对我有所帮助:http://jimhoskins.com/2012/12/17/angularjs-and-apply.html.事实上,如果我理解的话,根本不应该要求申请,因为我的数据来自$ http已经提供了这个.所以,我最后只是在成功回调中设置范围项变量.这是完整的JS,希望这可以帮助像我这样的新人.现在我将使用TypeScript模型,服务和所有现实世界的东西扩展测试:我担心我将不得不做一些新的帖子...... :)

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: "id", displayName: "ID", width: "60" },
            { 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 sb = [];
            for (var i = 0; i < $scope.sortOptions.fields.length; i++) {
                sb.push($scope.sortOptions.directions[i] === "DESC" ? "-" : "+");
                sb.push($scope.sortOptions.fields[i]);
            }

            var p = {
                name: $scope.filterOptions.filterText,
                pageNumber: $scope.pagingOptions.currentPage,
                pageSize: $scope.pagingOptions.pageSize,
                sortInfo: sb.join("")
            };

            $http({
                url: "/api/item",
                method: "GET",
                params: p
            }).success(function(data, status, headers, config) {
                $scope.totalServerItems = data.totalItems;
                $scope.items = data.items;
            }).error(function(data, status, headers, config) {
                alert(JSON.stringify(data));
            });
        }, 100);
    };

    // watches
    $scope.$watch('pagingOptions', function (newVal, oldVal) {
        if (newVal !== oldVal) {
            $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();
}]);
Run Code Online (Sandbox Code Playgroud)

(作为旁注,你可以从代码中看到我传递一个字符串用于排序数据,而不是两个数组用于字段和方向.事实上,我找不到接收数组作为输入成员的正确方法在C#控制器中的模型;所以我只是传递一个字符串,其中每个字段名称都以+或 - 为前缀,根据上升/下降方向).


Cha*_*ani 4

您将 ng-grid 上的数据源设置为items,但您永远不会在服务器成功回调上更新项目数组。

成功回调时做这样的事情

$scope.totalServerItems = data.totalItems;
angular.forEach(data.items, function(item) {
   $scope.items.push(item);
});
Run Code Online (Sandbox Code Playgroud)