Blazor QuickGrid でリッチなテーブルをサクッと作成する

# やりたいこと
- Blazor でページングやソート・フィルターを備えたリッチなテーブルを簡単に追加したい

# 環境
- VisualStudio 2022 Community 2022 Version 17.9.6
- .NET 8(C# 12)

# 方法
## QuickGrid
ASP.NET Core 8.0 で追加された QuickGrid を使います。

> QuickGrid コンポーネントは、データを表形式ですばやく効率的に表示するための Razor コンポーネントです。

https://learn.microsoft.com/ja-jp/aspnet/core/release-notes/aspnetcore-8.0?view=aspnetcore-8.0#quickgrid

詳細はこちら。

https://learn.microsoft.com/ja-jp/aspnet/core/blazor/components/quickgrid

機能はデモアプリで確認できます。

https://aspnet.github.io/quickgridsamples/

これは期待できそうです。


:::note
QuickGrid 自体は .NET 7 から試験的に追加されていました。
:::

https://learn.microsoft.com/ja-jp/aspnet/core/release-notes/aspnetcore-7.0?view=aspnetcore-7.0#experimental-quickgrid-component

## プロジェクトの作成
今回は Blazor Web Assembly を使います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1107938/4677853d-cae4-f841-a9cb-b584f20fba40.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1107938/262c10db-9b74-4085-9bef-f1416bdf0dc2.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1107938/10ed0da0-36df-505c-eea7-bfddce619737.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1107938/db9d1500-62ec-09f7-3295-98bbdcfe401e.png)

実行してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1107938/38bcf85c-e940-d02b-dc40-f3a86f4d0821.png)

## サンプルコードをコピペ

アプリにページを追加し、先ほどの [ドキュメント](https://learn.microsoft.com/ja-jp/aspnet/core/blazor/components/quickgrid) からサンプルコードをコピペしていきます。

### Razor コンポーネントを追加

1. Pages フォルダーで右クリック→追加→Razor コンポーネント
     ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1107938/305d757a-2b1f-f287-bcb4-bde5f7227805.png)
1. `QuickGridPage.razor` を追加

```diff_html:QuickGridPage.razor
<h3>QuickGridPage</h3>

@code {

}
```

### ページに設定

1. `@page` と `PageTitle` を設定（ついでに見出しも `h1` に）
```diff_html:QuickGridPage.razor
+@page "/quick-grid"
+
+<PageTitle>Quick Grid</PageTitle>
+
-<h3>QuickGridPage</h3>
+<h1>QuickGridPage</h1>

@code {

}
```

### ナビゲーションに追加

1. `NavMenu.razor` に `QuickGridPage` を追加
```diff_html:NavMenu.razor
<div class="@NavMenuCssClass nav-scrollable" @onclick="ToggleNavMenu">
    <nav class="flex-column">
        <div class="nav-item px-3">
            <NavLink class="nav-link" href="" Match="NavLinkMatch.All">
                <span class="bi bi-house-door-fill-nav-menu" aria-hidden="true"></span> Home
            </NavLink>
        </div>
        <div class="nav-item px-3">
            <NavLink class="nav-link" href="counter">
                <span class="bi bi-plus-square-fill-nav-menu" aria-hidden="true"></span> Counter
            </NavLink>
        </div>
        <div class="nav-item px-3">
            <NavLink class="nav-link" href="weather">
                <span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> Weather
            </NavLink>
        </div>
+        <div class="nav-item px-3">
+            <NavLink class="nav-link" href="quick-grid">
+                <span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> Quick Grid
+            </NavLink>
+        </div>
    </nav>
</div>
```
:::note
アイコンは追加が面倒なので `Weather` のものをそのまま使います。
:::
2\. 動作確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1107938/73ba11ae-ef0d-f6d4-3a29-23b34337761d.png)

### NuGet パッケージの追加

1. プロジェクトを右クリック→パッケージの管理
1. 参照で `Microsoft.AspNetCore.Components.QuickGrid` を検索、インストール
:::note
2024/05/06 現在の最新版は 8.0.4 です。
:::

### サンプルコードをコピペ
1. サンプルコードのテーブルをコピペ
 
```diff_html:QuickGridPage.razor
@page "/quick-grid"
+@using Microsoft.AspNetCore.Components.QuickGrid

<PageTitle>Quick Grid</PageTitle>

<h1>QuickGridPage</h1>

-@code {
-
-}
+<QuickGrid Items="@people">
+    <PropertyColumn Property="@(p => p.PersonId)" Sortable="true" />
+    <PropertyColumn Property="@(p => p.Name)" Sortable="true" />
+    <PropertyColumn Property="@(p => p.PromotionDate)" Format="yyyy-MM-dd" Sortable="true" />
+</QuickGrid>
+
+@code {
+    private record Person(int PersonId, string Name, DateOnly PromotionDate);
+
+    private IQueryable<Person> people = new[]
+    {
+        new Person(10895, "Jean Martin", new DateOnly(1985, 3, 16)),
+        new Person(10944, "António Langa", new DateOnly(1991, 12, 1)),
+        new Person(11203, "Julie Smith", new DateOnly(1958, 10, 10)),
+        new Person(11205, "Nur Sari", new DateOnly(1922, 4, 27)),
+        new Person(11898, "Jose Hernandez", new DateOnly(2011, 5, 3)),
+        new Person(12130, "Kenji Sato", new DateOnly(2004, 1, 9)),
+    }.AsQueryable();
+}
```

2\. 動作確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1107938/6487b527-44fa-280a-cb6f-2fc5100dda50.png)
テーブルが表示されました。

:::note
表示がうまくいかない場合はスーパーリロードしましょう。
:::

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1107938/a1f1320b-5c99-dd68-8a0c-9e0c1e5e3067.png)
各列のタイトルをクリックするとソートできます。

いとも簡単に使い始めることができました。

## 機能を追加する

[デモアプリ](https://aspnet.github.io/quickgridsamples/) に倣って機能を追加していきます。

### ページング

https://aspnet.github.io/quickgridsamples/paging

シンプルなページング機能を追加します。

```diff_html:QuickGrid.razor
@page "/quick-grid"
@using Microsoft.AspNetCore.Components.QuickGrid

<PageTitle>Quick Grid</PageTitle>

<h1>QuickGridPage</h1>

-<QuickGrid Items="@people">
+<QuickGrid Items="@people" Pagination="@_paginationState">
    <PropertyColumn Property="@(p => p.PersonId)" Sortable="true" />
    <PropertyColumn Property="@(p => p.Name)" Sortable="true" />
    <PropertyColumn Property="@(p => p.PromotionDate)" Format="yyyy-MM-dd" Sortable="true" />
</QuickGrid>

+<Paginator State="@_paginationState" />

@code {
    private record Person(int PersonId, string Name, DateOnly PromotionDate);

    private IQueryable<Person> people = new[]
    {
        new Person(10895, "Jean Martin", new DateOnly(1985, 3, 16)),
        new Person(10944, "António Langa", new DateOnly(1991, 12, 1)),
        new Person(11203, "Julie Smith", new DateOnly(1958, 10, 10)),
        new Person(11205, "Nur Sari", new DateOnly(1922, 4, 27)),
        new Person(11898, "Jose Hernandez", new DateOnly(2011, 5, 3)),
        new Person(12130, "Kenji Sato", new DateOnly(2004, 1, 9)),
    }.AsQueryable();

+    
+    private PaginationState _paginationState = new()
+    {
+        ItemsPerPage = 2,
+    };
}
```

#### 動作確認

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1107938/dc31aa52-67b9-ce8b-6d57-d9471f899f1c.png)

この数行の変更で以下が追加されます。
- テーブル下部に水平線
- 総アイテム数
- ページセレクター

### フィルター

https://aspnet.github.io/quickgridsamples/filtering

名前でフィルターできるように追加します。

```diff_html:QuickGrid.razor
@page "/quick-grid"
@using Microsoft.AspNetCore.Components.QuickGrid

<PageTitle>Quick Grid</PageTitle>

<h1>QuickGridPage</h1>

-<QuickGrid Items="@people" Pagination="@_paginationState">
+<QuickGrid Items="@FilteredCountries" Pagination="@_paginationState">
    <PropertyColumn Property="@(p => p.PersonId)" Sortable="true" />
-    <PropertyColumn Property="@(p => p.Name)" Sortable="true" />
+    <PropertyColumn Property="@(p => p.Name)" Sortable="true">
+        <ColumnOptions>
+            <div class="search-box">
+                <input type="search" autofocus @bind="_nameFilter" @bind:event="oninput" placeholder="Name..." />
+            </div>
+        </ColumnOptions>
+    </PropertyColumn>
    <PropertyColumn Property="@(p => p.PromotionDate)" Format="yyyy-MM-dd" Sortable="true" />
</QuickGrid>

<Paginator State="@_paginationState" />

@code {
    private record Person(int PersonId, string Name, DateOnly PromotionDate);

    private IQueryable<Person> people = new[]
    {
        new Person(10895, "Jean Martin", new DateOnly(1985, 3, 16)),
        new Person(10944, "António Langa", new DateOnly(1991, 12, 1)),
        new Person(11203, "Julie Smith", new DateOnly(1958, 10, 10)),
        new Person(11205, "Nur Sari", new DateOnly(1922, 4, 27)),
        new Person(11898, "Jose Hernandez", new DateOnly(2011, 5, 3)),
        new Person(12130, "Kenji Sato", new DateOnly(2004, 1, 9)),
    }.AsQueryable();

    private PaginationState _paginationState = new()
    {
        ItemsPerPage = 2,
    };
+
+    private string _nameFilter = "";
+
+    IQueryable<Person> FilteredCountries
+    {
+        get
+        {
+            if (string.IsNullOrEmpty(_nameFilter))
+            {
+                return people;
+            }
+
+            IQueryable<Person> result = people.Where(c => c.Name.Contains(_nameFilter, StringComparison.CurrentCultureIgnoreCase));
+
+            return result;
+        }
+    }
}
```

#### 動作確認

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1107938/c1403700-181e-5aca-5eb3-4ddb075f333a.png)

Name 列にハンバーガーボタンが追加され、その中にインプットボックスがあります。Name 列を任意文字列でフィルターできるようになりました。

# まとめ

Blazor QuickGrid を使うことで「ソート」「ページング」「フィルター」を備えたリッチなテーブルを簡単に実装することができました。公式のコンポーネント、もっと増えていってほしいですね。