服务器端分页+过滤+分拣NG-网格的WebAPI [英] Server-side paging+filtering+sorting for ng-grid with WebAPI

查看:265
本文介绍了服务器端分页+过滤+分拣NG-网格的WebAPI的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图创建一个使用NG-网格ASP.NET的WebAPI的一个简单的工作示例。因此,我从服务器端分页的例子开始在NG-电网的例子页面(的http://角UI。 github.io/ng-grid/ );反正,我总是格显示空列,即使调试我可以证实的数据被正确接收时。也许我只是缺少在网格中设置的东西,但所有的样品,我发现类似于我的。谁能帮助吗?下面是我所做的:

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:

更新#1 :建议的解决方案似乎工作,但只适用于第1页。每当我移动到一个新的页面或做任何其他操作需要刷新,显示的数据保持,即使服务器如预期返回的数据变化是一样的。此外,从所有code样品,我发现好像设置数据的正确方法就是更换阵列成员价值,而不是清空,再填充它。我试图与适用,在<建议href=\"https://groups.google.com/forum/#!searchin/angular/nggrid/angular/vUIfHWt4s_4/oU_C9w8j-uMJ\">https://groups.google.com/forum/#!searchin/angular/nggrid/angular/vUIfHWt4s_4/oU_C9w8j-uMJ,但我得到了相同的结果。

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.

只要创建一个新的应用程序MVC4,更新的NuGet包,并添加角度和NG-格包。
我的假数据模型是由项目类psented重新$ P $:

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; }
}

我还加了几个模型分页处理,过滤和排序不同的数据集(我觉得更容易有一个共同的分页示范基地-PagedFilter-,以及一些衍生车型):

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;
    }
}

下面是ItemFilter:

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; }
}

然后我加入一个API控制器来获得项目:

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()
                   };
    }
}

客户端

在客户端,我的角应用就是仿照NG网样本单个控制器:所以我直接将属性添加到$范围,即使在真实的场景中,我宁愿使用一个模型(可能从打字稿类产生)。 HTML:

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();
}]);

在我的code,成功回调被调用,我可以浏览data.items返回的所有项目。然而,没有什么是显示在网格中。出现在控制台没有错误。

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.

推荐答案

实验了一下之后,我想我找到了正确的code。这篇文章约$应用帮了我一下:<一href=\"http://jimhoskins.com/2012/12/17/angularjs-and-apply.html\">http://jimhoskins.com/2012/12/17/angularjs-and-apply.html.事实上,如果我的理解以及调用应用不应该在所有需要的,因为我的数据是从$ HTTP它已经提供了这个未来。所以,我只设置范围的项目变量的成功回调结束。下面是再次充分JS,希望这可以帮助一些新人和我一样。现在,我要与打字稿模型,服务和所有现实世界的东西扩展测试:我怕我会做出一些新的帖子...:)

After experimenting a bit, I think I found the correct code. This post about $apply helped me a bit: http://jimhoskins.com/2012/12/17/angularjs-and-apply.html. In fact, if I understand well the call to apply should not be needed at all, given that my data are coming from $http which already provides this. So, I ended with just setting the scope items variable in the success callback. Here is the full JS again, hope this can help some newcomer like me. Now I'm going to expand the test with TypeScript models, services and all the real-world stuff: I fear I'll have to make some new post... :)

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();
}]);

(一点题外话,你可以从我传递一个字符串类型的数据,而不是两个数组的领域和方向的code看到的。事实上,我找不到接收的正确方法数组作为在C#控制器我输入模型的成员,所以我只是路过一个字符串,其中每个字段名称为$ p $由+ pfixed或 - /按照升序降序方向)

(As a sidenote, you can see from the code that I'm passing a single string for sort data, rather than two arrays for fields and directions. In fact, I could not find the right way of receiving arrays as members of my input model in the C# controller; so I'm just passing a single string where each field name is prefixed by + or - according to the ascending/descending direction).

这篇关于服务器端分页+过滤+分拣NG-网格的WebAPI的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆