2013年12月3日 星期二

ASP.NET MVC 使用政府公開資料 Part.3 - 資料分頁 使用 PagedList 的 StaticPagedList

上一篇「ASP.NET MVC 使用政府公開資料 Part.2 - 增加條件過濾」已經完成了條件過濾的功能,但是一堆資料全部攤在眼前,我想不是每個人都能全部看完,所以在瀏覽資料時如果有分頁功能還是會比較好一些,而這一次所加入的分頁功能是使用之前有介紹過得 PagedList,但不會使用一般我們常用的 ToPagedList 而是使用 StaticPagedList。

對於 PagedList 這個套件不熟悉的朋友,可以先看之前的文章:

ASP.NET MVC 資料分頁 - 使用 PagedList.Mvc

ASP.NET MVC 資料分頁 - 使用 PagedList.Mvc:分頁列樣式

ASP.NET MVC 資料分頁 - 使用 PagedList.Mvc:自訂分頁資訊顯示

ASP.NET MVC 資料分頁 - 使用 PagedList.Mvc:AJAX

ASP.NET MVC 資料分頁 - 使用 PagedList.Mvc:Table 的 Checkbox

 


一般我們使用 PagedList 的分頁都會是使用 ToPagedList,

https://github.com/troygoode/PagedList/blob/master/src/PagedList/PagedList.cs

https://github.com/troygoode/PagedList/blob/master/src/PagedList/PagedListExtensions.cs

這是屬於動態處理的分頁方式,我們只要提供 IQueryable<T> 類型的資料以及 PageNumber, PageSize,其餘的工作都交由 PagedList 作處理。

而 StaticPagedList 則與 PagedList 有很大的不同,我們必須自己處理資料的分頁、取得資料總數的操作,StaticPagedList 就只有進行分頁功能的處理,例如計算是否有上下頁、分頁頁數等 MetaData,另外有些資料的取得或提供並不是有提供 IQueryable<T> 類別的資料,雖然使用 AsQueryable<T> 做轉型,但指因為要做資料分頁將已取得的資料轉為 IQueryable<T> 型別就會顯得多此一舉,所以這樣的情況就可以使用 StaticPagedList,

https://github.com/TroyGoode/PagedList/blob/master/src/PagedList/StaticPagedList.cs

https://github.com/troygoode/PagedList/blob/master/src/PagedList/BasePagedList.cs

以我們這個資料來源取自政府公開資料並且將資料存放到快取中的範例就蠻適合使用 StaticPagedList。

 

首先在專案裡透過 NuGet 加入 PagedList 與 PagedList.Mvc,

image

 

然後在 Index Action 方法裡加入 StaticPagedList 的使用,

image

public async Task<ActionResult> Index(int? page, string districts, string types, string companys)
{
    //區域
    ViewBag.Districts =
        await this.GetSelectList(await this.GetDistricts(), districts);
    ViewBag.SelectedDistrict = districts;
 
    //熱點分類
    var typeSelectList =
        await this.GetSelectList(await this.GetHotSpotTypes(), types);
    ViewBag.Types = typeSelectList.ToList();
    ViewBag.SelectedType = types;
 
    //業者
    var companySelectList =
        await this.GetSelectList(await this.GetCompanys(), companys);
    ViewBag.Companys = companySelectList.ToList();
    ViewBag.SelectedCompany = companys;
 
    var source = await this.GetHotSpotData();
    source = source.AsQueryable();
 
    if (!string.IsNullOrWhiteSpace(districts))
    {
        source = source.Where(x => x.District == districts);
    }
    if (!string.IsNullOrWhiteSpace(types))
    {
        source = source.Where(x => x.Type == types);
    }
    if (!string.IsNullOrWhiteSpace(companys))
    {
        source = source.Where(x => x.Company == companys);
    }
 
    int pageIndex = page ?? 1;
    int pageSize = 10;
    int totalCount = 0;
 
    totalCount = source.Count();
 
    source = source.OrderBy(x => x.District)
                   .Skip((pageIndex - 1) * pageSize)
                   .Take(pageSize);
 
    var pagedResult = new StaticPagedList<HotSpot>(source, pageIndex, pageSize, totalCount);
 
    return View(pagedResult);
}

 

而在檢視頁面里,如果在 Controller 裡是使用 ToPagedList 的話,在檢視頁面的 model 就要使用 IPagedList<T> 型別,而如果是使用 StaticPagedList 的話,檢視頁面則要改用 StaticPagedList<T> 型別,如下:

image
而至於 PagedListPager 的部份則是一樣使用,但我們在頁面上有三個條件過濾的下拉選單,所以在 PagedListPager 裡要增加 RouteValues 的內容,
image
完整的檢視頁面原始碼
@using PagedList
@using PagedList.Mvc
@model PagedList.StaticPagedList<BlogSample.Models.HotSpot>
 
@{
    ViewBag.Title = "Index";
}
 
<div class="row">
    <h2>
        練習題 - 新北市 WIFI 熱點
        <small>
            資料來源:
            <a href="http://data.ntpc.gov.tw/NTPC/" target="_blank">
                新北市政府資料開放平台
            </a>
            -
            <a href="http://data.ntpc.gov.tw/NTPC/od/query?_m=detail&oid=04958686-1B92-4B74-889D-9F34409B272B" target="_blank">
                新北市WIFI熱點
            </a>
        </small>
    </h2>
</div>
<hr />
<div class="row well">
    <form class="form" role="form">
        <div class="col-lg-3">
            <div class="input-group">
                <span class="input-group-addon">區域</span>
                @Html.DropDownList("Districts", null, "全部", new { @class = "form-control" })
            </div>
        </div>
        <div class="col-lg-3">
            <div class="input-group">
                <span class="input-group-addon">熱點分類</span>
                @Html.DropDownList("Types", null, "全部", new { @class = "form-control" })
            </div>
        </div>
        <div class="col-lg-4">
            <div class="input-group">
                <span class="input-group-addon">業者</span>
                @Html.DropDownList("Companys", null, "全部", new { @class = "form-control" })
            </div>
        </div>
        <div class="col-lg-2">
            <button type="submit" id="ButtonSubmit" class="btn btn-primary">
                <span class="glyphicon glyphicon-search"></span> 查詢
            </button>
        </div>
    </form>
</div>
 
@Html.PagedListPager((IPagedList)Model,
        page => Url.Action("Index", new
        {
            page = page,
            districts = ViewBag.SelectedDistrict,
            types = ViewBag.SelectedType,
            company = ViewBag.SelectedCompany
        }),
        PagedListRenderOptions.ClassicPlusFirstAndLast)
 
<table class="table table-striped table-hover">
    <thead>
        <tr style="background-color: #d3efff;">
            <th>
                @Html.DisplayNameFor(model => model.FirstOrDefault().Spot_Name)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.FirstOrDefault().Type)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.FirstOrDefault().Company)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.FirstOrDefault().District)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.FirstOrDefault().Address)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.FirstOrDefault().Apparatus_Name)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.FirstOrDefault().Latitude)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.FirstOrDefault().Longitude)
            </th>
        </tr>
    </thead>
    <tbody>
 
        @foreach (var item in Model)
        {
            <tr>
                <td>
                    @Html.DisplayFor(modelItem => item.Spot_Name)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Type)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Company)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.District)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Address)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Apparatus_Name)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Latitude)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Longitude)
                </td>
            </tr>
        }
    </tbody>
</table>

 

執行結果

image

image

image

 

到此為止就完成了這一系列練習的內容,我們完成了基本資料的取得、過濾、分頁、快取以及非同步等操作,其他進階的操作功能就讓大家自己發想與實作。

 

以上

5 則留言:

  1. 不好意思,請問有無原始Controller檔案可供下載觀看
    我不太懂 await this.GetSelectList(await this.GetCompanys(), companys);這段
    我是使用vs2012 開發mvc4.5網頁的~
    懇請回覆,感恩。

    回覆刪除
    回覆
    1. https://github.com/kevintsengtw?tab=repositories
      已經逐步把部落格的範例程式放到 GitHub 並且公開,可直接瀏覽程式或下載原始碼

      刪除
    2. Hello, 不清楚你所謂的不懂是指哪一個地方不懂呢?
      有看到 await 的修飾詞,就表示該方法有用 async 非同步的處理,
      有關 async 的資訊可以參考「MSDN - 使用 Async 和 Await 設計非同步程式 (C# 和 Visual Basic)」]
      http://msdn.microsoft.com/zh-tw/library/hh191443(v=vs.120).aspx

      刪除
    3. 感謝回覆,至於主要問題是,我在view上點選查詢,但是都無作用
      是否在public async Task Index(.....)上面,要另加上[HttpPost]
      附View截圖http://ppt.cc/F2je
      PS:我是使用 using (Html.BeginForm("Index", "Home")){.....}的方式,是否這邊需做修正?

      刪除
    4. 其實你的功能不一定要使用 async,因為目前這一篇文章是有用到第三方的資料,所以才會用到 async 方法,
      至於你的問題,我會覺得不是出現在 View 上面,你可以開啟瀏覽器的 Developer Tool 來觀察資料是否送出,
      再來就是問題有可能會是出現在 Controller 裡,看看是否有建立與 Form 對應 HttpPost 的 Action 方法,
      Html Helper 的 BeginForm 預設的 FormMethod 為 Post,因為你沒有給我看 Controller 的程式,所以我就先這樣推斷,
      而我在這一篇是直接使用 Html 的 Form Tag,未指定 Method 時,預設會使用 Get 的方式往後端傳遞資料,
      不過我放在 GitHub 的原始檔裡有另一個 AnotherController.cs 與 View,
      情境就是使用 Html Helper 的 BeginForm,然後 FormMethod 指定使用 POST,
      如果還是有問題無法解決(因為你的程式跟這一篇文章所說的情境是不一樣的),
      因為這裡不是討論區,所以請使用部落格左方的「詢問與建議」功能。
      你應該要看的是這一篇才對「練習題 - ASP.NET MVC 資料列表顯示 + 分頁 + 查詢 + FormMethod.Post」(類似的情境)
      http://kevintsengtw.blogspot.tw/2014/10/aspnet-mvc-formmethodpost.html

      刪除

提醒

千萬不要使用 Google Talk (Hangouts) 或 Facebook 及時通訊與我聯繫、提問,因為會掉訊息甚至我是過了好幾天之後才發現到你曾經傳給我訊息過,請多多使用「詢問與建議」(在左邊,就在左邊),另外比較深入的問題討論,或是有牽涉到你實作程式碼的內容,不適合在留言板裡留言討論,請務必使用「詢問與建議」功能(可以夾帶檔案),謝謝。