Contents

使用PagedList與vue-infinite-loading,製作無限載入(滾動分頁)

使用PagedList與vue-infinite-loading,製作無限載入

(滾動分頁)

PagedList是.NET開發環境中,老牌的分頁套件,早期.NET MVC 5官方教學在分頁的說明上也有使用這套件進行分頁,簡單易用且支援Bootstrap,隨便搜尋也可以找到不少的使用範例,這邊就不多介紹。.NET Core開始,PagedList就將在前端顯示的分頁套件切割出來,要使用傳統分頁樣式要多安裝X.PagedList.Web.Common,也因為是分割開來,就代表我們不一定要使用傳統的分頁樣式,特別是以手機為主的畫面,傳統的頁碼顯示相對不友善,且容易跑版。

以手機顯示為主的分頁,比較常見的幾種作法是,不使用頁碼,僅保留上一頁、下一頁,另一種就是無限載入的方式(滾動分頁)。滾動分頁這需求的套件倒也不少,自己評估幾套後,覺得vue-infinite-loading感覺最易用,這邊就記錄一下,盡量以簡單的方式達到滾動分頁。

特別說明一下,這篇並沒有使用vue-cli,一方面是剛接觸Vue不久,很多觀念還在建立中,另一個原因是新增出來的專案我覺得有點肥大,小型專案感覺不太適用。

環境

實作

  1. 先安裝X.PagedList,.NET Core,直接安裝Core版本就好

    /static/使用PagedList與vue-infinite-loading_製作無限載入_滾動分頁_da8b66d35bf546568e4af6d822c6516c/2020-10-14_17-12-28.png
  2. 從資料庫取得要分頁的資料

    1
    2
    3
    4
    
    public IQueryable<School> GetStuffFromDatabase()
    {
        return _context.School.AsNoTracking();
    }
    
  3. 依照PagedList範例設置分頁

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    
    protected IPagedList<School> GetPagedSchools(int? page)
    {
        if (page.HasValue && page < 1)
            return null;
    
        var listUnpaged = GetStuffFromDatabase().AsEnumerable();
    
        // 設置每5筆分頁
        const int pageSize = 5;
        var listPaged = listUnpaged.ToPagedList(page ?? 1, pageSize);
    
        if (listPaged.PageNumber != 1 && page.HasValue && page > listPaged.PageCount)
            return null;
    
        return listPaged;
    }
    

    原本X.PagedList有說明頁的,目前似乎不見,倒也不影響,網路上也有不少教學,Source Code中也能找到範例

  4. 前端的部分,先載入我們要的檔案

    1
    2
    3
    
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    @* @符號在Razor有特殊作用所以連結多一個@ *@
    <script src="https://unpkg.com/vue-infinite-loading@@^2/dist/vue-infinite-loading.js"></script>
    
  5. 然後製作一個顯示樣板

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    
    <ul class="list-unstyled">
        <li class="media my-3" v-for="item in schools">
            <div class="media-body">
                <a :href="'/Details/' + item.Id" class="list-group-item list-group-item-action shadow-sm rounded scorelist">
                    <h5 class="mt-0"> {{item.SchoolName}} {{item.Department}} </h5>
                    <div> 連絡電話: {{item.Phone}} </div>
                    <div> 網址: {{item.WebSite}} </div>
                </a>
            </div>
        </li>
    </ul>
    <infinite-loading v-on:infinite="infiniteHandler">
        <div slot="no-more"> -- 已查無資料 -- </div>
    </infinite-loading>
    
  6. Javascript部分,一樣官方範例照抄,再改成我們要的就好

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    
    infiniteHandler($state) {
        let url = `/index?pageNumber=${this.page}&handler=Schools`;
        fetch(url).then(response => response.json())
            .then(result => {
                if (result.success && result.count >= 1) {
                    this.page += 1;
                    let data = JSON.parse(result.responseText);
    				// Spread syntax https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Operators/Spread_syntax
                    this.schools.push(...data);
                    $state.loaded();
                } else {
                    $state.complete();
                }
            });
    },
    
  7. 結果,每五筆一分頁,這樣看效果沒有很明顯,但若開啟偵錯模式,就可以明顯看到頁碼的更動

    /static/使用PagedList與vue-infinite-loading_製作無限載入_滾動分頁_da8b66d35bf546568e4af6d822c6516c/2020-10-14_18-24-37.gif

完整程式碼

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
<div id="app">
    <ul class="list-unstyled">
        <li class="media my-3" v-for="item in schools">
            <div class="media-body">
                <a :href="'/Details/' + item.Id" class="list-group-item list-group-item-action shadow-sm rounded scorelist">
                    <h5 class="mt-0"> {{item.SchoolName}} {{item.Department}} </h5>
                    <div> 連絡電話: {{item.Phone}} </div>
                    <div> 網址: {{item.WebSite}} </div>
                </a>
            </div>
        </li>
    </ul>
    <infinite-loading v-on:infinite="infiniteHandler">
        <div slot="no-more"> -- 已查無資料 -- </div>
    </infinite-loading>
</div>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
@* @符號在Razor有特殊作用所以連結多一個@ *@
<script src="https://unpkg.com/vue-infinite-loading@@^2/dist/vue-infinite-loading.js"></script>

<script>
    new Vue({
        el: '#app',
        data: {
            schools: [],
            page: 1
        },
        methods: {
            infiniteHandler($state) {
                let url = `/index?pageNumber=${this.page}&handler=Schools`;
                fetch(url).then(response => response.json())
                    .then(result => {
                        if (result.success && result.count >= 1) {
                            this.page += 1;
                            let data = JSON.parse(result.responseText);
                            // Spread syntax https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Operators/Spread_syntax
                            this.schools.push(...data);
                            $state.loaded();
                        } else {
                            $state.complete();
                        }
                    });
            },
        }
    });
</script>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public class IndexModel : PageModel
{
    private readonly SchoolContext _context;

    public IndexModel(SchoolContext context)
    {
        _context = context;
    }

    public void OnGet()
    {
    }

    public IActionResult OnGetSchools(int pageNumber = 1)
    {
        // 對於Razor Page專案,page是指目前所處的頁面(/index),所以改一個參數名稱
        var data = GetPagedSchools(pageNumber);

        return new JsonResult(new { success = true, responseText = JsonConvert.SerializeObject(data), count = data.Count() });
    }

    protected IPagedList<School> GetPagedSchools(int? page)
    {
        if (page.HasValue && page < 1)
            return null;

        var listUnpaged = GetStuffFromDatabase();

        // 設置每5筆分頁
        const int pageSize = 5;
        var listPaged = listUnpaged.ToPagedList(page ?? 1, pageSize);

        if (listPaged.PageNumber != 1 && page.HasValue && page > listPaged.PageCount)
            return null;

        return listPaged;
    }

    private IQueryable<School> GetStuffFromDatabase()
    {
        return _context.School.AsNoTracking();
    }
}

參考

ASP.NET MVC 5 教學課程:使用 ASP.NET MVC 應用程式中的 Entity Framework 來加入排序、篩選和分頁

https://docs.microsoft.com/zh-tw/aspnet/mvc/overview/getting-started/getting-started-with-ef-using-mvc/sorting-filtering-and-paging-with-the-entity-framework-in-an-asp-net-mvc-application

Vue-infinite-loading 使用指南

https://peachscript.github.io/vue-infinite-loading/zh/guide/

Spread syntax (展開運算式)

https://es6.ruanyifeng.com/#docs/array

https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Operators/Spread_syntax