2014年4月30日 星期三

ASP.NET MVC 4 WebApi 與 Extjs 的結合 -- 換頁重新載入資料

在 Web Form 做 Grid 換頁時,通常會先將資料把所有資料先建入 DataTable,再使用控制項接收這些資料,換頁時,就將這些資料經過處理再顯示。然後再做資料搜尋,會再去要一次資料,如此一來,在每次產生 GridView 時,都會使用同一份資料篩選,等於是網頁上有一份資料在做處理,如果資料量一大,吃的資源也大,處理與顯示時相對的就慢。

使用 Extjs 做 Grid.Panel 分頁,一般來說會先將所有資料載到頁面,再做分頁,只是 Extjs 接收資料格式必須要在最根層加上總數量,這樣才能知道該頁顯示幾頁,總共有幾頁,所以格式必須符合如下:
{
    "totalCount": "6679",
    "topics": [
    {
        // ....
    },
    {
        // ....
    },
    {
        // ....
    },
    {
        // ....
    },
    {
        // ....
    }
    // ...
    ]
}

而符合這種格式必須在 Web Api 段做些許調整,要多寫一個屬性讓 API 能使用,可以參考:Web API, OData, $inlinecount and testing,程式碼如下

public class InlineCountQueryableAttribute : QueryableAttribute
{
    private static MethodInfo _createPageResult =
        typeof(InlineCountQueryableAttribute)
        .GetMethods(BindingFlags.Static | BindingFlags.NonPublic)
        .Single(m => m.Name == "CreatePageResult");

    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {
        base.OnActionExecuted(actionExecutedContext);

        HttpRequestMessage request = actionExecutedContext.Request;
        HttpResponseMessage response = actionExecutedContext.Response;

        IQueryable result;
        if (response.IsSuccessStatusCode
            && response.TryGetContentValue<IQueryable>(out result))
        {
            long? inlineCount = request.GetInlineCount();
            if (inlineCount != null)
            {
                actionExecutedContext.Response = _createPageResult.MakeGenericMethod(result.ElementType).Invoke(
                    null, new object[] { request, request.GetInlineCount(), request.GetNextPageLink(), result }) as HttpResponseMessage;
            }
        }
    }

    internal static HttpResponseMessage CreatePageResult<T>(HttpRequestMessage request, long? count, Uri nextpageLink, IEnumerable<T> results)
    {
        return request.CreateResponse(HttpStatusCode.OK, new PageResult<T>(results, nextpageLink, count));
    }
}

在 API 加上此屬性:
[InlineCountQueryable]
public IEnumerable GetEmployees()
{
    IEnumerable employees = db.Employees.ToList();

    foreach (Employees employee in employees)
    {
        employee.Employees1 = null;
        employee.Employees2 = null;
    }

    return employees;
}

執行結果如下:
{ 
    Count: 15,
    Items: [
        { ... }, 
        { ... }, 
        ...
    ],
    NextPageLink: XXX,
}

這樣輸出結果與 Extjs Grid.Panel 做分頁要接收的格式已經 95% 相似了,而換頁時通常自動會帶 page 、 start 和 limit,page 感覺只是顯示用,而 start 和 limit 的部分可以使用 API 的 Queryable 來解決,它等同於 Queryable 的 $skip 和 $top:

首先我們先看如何在 grid.Panel 加上分頁,加上一個屬性即可,其中 store 為資料來源:
bbar: Ext.create('Ext.PagingToolbar', {
    store: store,
    displayInfo: true,
    displayMsg: 'Displaying topics {0} - {1} of {2}',
    emptyMsg: "No topics to display",
}),

因為每一次換頁就會與 store 要一次資料,所以必須針對來源做修改:
var store = Ext.create('Ext.data.JsonStore', {
    storeId: 'store',
    model: 'Model',
    pageSize: 3,
    
    proxy: {
        type: 'ajax',
        url: 'http://localhost:8090/api/Control?$inlinecount=allpages',
        reader: {
            type: 'json',
            root: 'Items',
            totalProperty: 'Count',

        },
        startParam: '$skip',
        limitParam: '$top'
    },
    autoLoad: true,
    listeners: {
        load: function (store, records, options) {
        }
    },

});

其中 PageSize 為每頁顯示數量,而 startParam、limitParam 則是改變它換頁帶的參數,最後執行就可以看到每換一次頁就會重新與 API 要一次資料,這樣一來頁面負擔不會太重,同時又做到速度與流量兼顧的結果。


沒有留言 :

張貼留言

Related Posts Plugin for WordPress, Blogger...