ASP.NET Core × Bootstrap Table でモデルデータをテーブルに表示する

[ASP.NET Core](https://www.asp.net/core/overview/aspnet-vnext)と[Bootstrap Table](http://bootstrap-table.wenzhixin.net.cn/)を組み合わせた忘備録。

環境
Visual Studio 2017
☆ASP.NET Core 1.1
★bootstrap 3.3.7
★bootstrap-table 1.11.1
★jquery 3.2.1

☆NuGetからインストール(あるいは標準でインストールされているもの)
★Bowerからインストール(あるいは標準でインストールされているもの)

※基本、以下コード例はVisual Studio -> 新規プロジェクト作成 -> 「ASP.NET Core Web アプリケーション(.NET Core)」選択時に自動で生成されるサンプルコード一式をそのまま流用しています。

# モデルデータを表示する

## コード例
ビューモデル

```csharp:Models/TableViewModel.cs
using System;
using System.ComponentModel;

namespace BootstrapTableApplication.Models
{
    public class TableViewModel
    {
        [DisplayName("学籍番号")]
        public int No { get; set; }

        [DisplayName("名前")]
        public string Name { get; set; }

        [DisplayName("誕生日")]
        public DateTime BirthDay { get; set; }
    }
}
```

コントローラー

```csharp:Controllers/HomeController.cs
using BootstrapTableApplication.Models;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;

namespace BootstrapTableApplication.Controllers
{
    public class HomeController : Controller
    {
        public IActionResult Index()
        {
            List<TableViewModel> viewModels = new List<TableViewModel>
            {
                new TableViewModel
                {
                    No = 1,
                    Name = "桐ヶ谷 和人",
                    BirthDay = DateTime.Parse("2008年10月7日"),
                },
                new TableViewModel
                {
                    No = 2,
                    Name = "結城 明日奈",
                    BirthDay = DateTime.Parse("2007年9月30日"),
                },
                new TableViewModel
                {
                    No = 3,
                    Name = "桐ヶ谷 直葉",
                    BirthDay = DateTime.Parse("2009年4月19日"),
                },
                new TableViewModel
                {
                    No = 4,
                    Name = "朝田 詩乃",
                    BirthDay = DateTime.Parse("2009年8月21日"),
                },
                new TableViewModel
                {
                    No = 5,
                    Name = "紺野 木綿季",
                    BirthDay = DateTime.Parse("2011年5月23日"),
                },
                new TableViewModel
                {
                    No = 6,
                    Name = "綾野 珪子",
                    BirthDay = DateTime.Parse("2010年10月4日"),
                },
                new TableViewModel
                {
                    No = 7,
                    Name = "篠崎 里香",
                    BirthDay = DateTime.Parse("2007年5月18日"),
                },
            };
            return View(viewModels);
        }

        public IActionResult About()
        {
            ViewData["Message"] = "Your application description page.";

            return View();
        }

        public IActionResult Contact()
        {
            ViewData["Message"] = "Your contact page.";

            return View();
        }

        public IActionResult Error()
        {
            return View();
        }
    }
}
```

HTML

```html:Views/Shared/_ViewImports.cshtml
@using BootstrapTableApplication
@using BootstrapTableApplication.Models
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
```
```html:Views/Home/Index.cshtml
@model IEnumerable<BootstrapTableApplication.Models.TableViewModel>

@{
    ViewData["Title"] = "Table View Page";
}

<h2>@ViewData["Title"]</h2>
<div class="table-responsive">
    <table id="table" class="table">
        <thead>
            <tr>
                <th data-field="no" data-sortable="true">@Html.DisplayNameFor(model => model.No)</th>
                <th data-field="name" data-sortable="true">@Html.DisplayNameFor(model => model.Name)</th>
                <th data-field="birthDay" data-sortable="true" data-formatter="dateFormatter">@Html.DisplayNameFor(model => model.BirthDay)</th>
            </tr>
        </thead>
    </table>
</div>

@section Scripts {
    <link rel="stylesheet" href="~/lib/bootstrap-table/dist/bootstrap-table.css" />
    <script src="~/lib/bootstrap-table/dist/bootstrap-table.js" type="text/javascript"></script>
    <script src="~/lib/bootstrap-table/dist/locale/bootstrap-table-ja-JP.js" type="text/javascript"></script>
    <script type="text/javascript">
        // モデルをJsonに変換し、BootstrapTableに読み込ませる
        $(function () {
            var table = @Html.Raw(Json.Serialize(Model));
            $('#table').bootstrapTable({
                data: table
            });
        });

        // 日付Formatter
        function dateFormatter(value, row, index) {
            return new Date(value).toLocaleDateString();
        }
    </script>
}
```

## 実行結果
![image.png](https://qiita-image-store.s3.amazonaws.com/0/76153/c5ddec31-b868-380c-0c5b-bbc167de0a6f.png)

楽ちん！

# [おまけ]表示形式を変える
セル内文字列の表示形式を加工したいときは、Thタグに「data-formatter」属性を付加し、基本Javascriptにて加工します。

例えば生年月日を和暦変換したい。

## コード例
```html:Views/Home/Index.cshtml
　：
（省略）
　：
                <th data-field="birthDay" data-sortable="true" data-formatter="dateFormatter">@Html.DisplayNameFor(model => model.BirthDay)</th>
　：
（省略）
　：
        // 日付Formatter
        function dateFormatter(value, row, index) {
            var date = new Date(value);
            var options = {
                year: "numeric",
                month: "short",
                day: "numeric",
            };
            return new Date(value).toLocaleDateString("ja-JP-u-ca-japanese", options);
        }
    </script>
}
```

## 実行結果
![image.png](https://qiita-image-store.s3.amazonaws.com/0/76153/22d30d07-c602-b653-0061-902b73b0252d.png)

数値(カンマ区切り)や金額(通貨記号)なども似たようにして加工できます。

# [おまけ]ページャー
Table属性に「data-pagination="true"」を追加するだけ。

## コード例
```html:Views/Home/Index.cshtml
　：
（省略）
　：
    <table id="table" class="table" data-pagination="true" data-page-size="5">
　：
（省略）
　：
```

## 実行例
![image.png](https://qiita-image-store.s3.amazonaws.com/0/76153/780a661b-9ebe-c456-a186-d856460603a0.png)

# [おまけ]data-fieldの値をTagHelperを使って求める
## コード例

HtmlHelper(@Html.*)に拡張メソッドを追加します。

```csharp:Models/HtmlHelperVariableNameExtensions.cs
using System;
using System.ComponentModel;

namespace BootstrapTableApplication.Models
{
    public static class HtmlHelperVariableNameExtensions
    {
        public static string VariableNameFor<TModelItem, TResult>(
            this IHtmlHelper<IEnumerable<TModelItem>> htmlHelper,
            Expression<Func<TModelItem, TResult>> expression)
        {
            if (htmlHelper == null)
            {
                throw new ArgumentNullException(nameof(htmlHelper));
            }

            var name = ((MemberExpression)expression.Body).Member.Name;
            return name.Substring(0, 1).ToLower() + name.Substring(1);
        }
    }
}
```
Thタグのdata-fieldに追加した拡張メソッド「@Html.VariableNameFor」を設定します。

```html:Views/Home/Index.cshtml
　：
（省略）
　：
                <th data-field="@Html.VariableNameFor(model => model.No)" data-sortable="true">@Html.DisplayNameFor(model => model.No)</th>
                <th data-field="@Html.VariableNameFor(model => model.Name)" data-sortable="true">@Html.DisplayNameFor(model => model.Name)</th>
                <th data-field="@Html.VariableNameFor(model => model.BirthDay)" data-sortable="true" data-formatter="dateFormatter">@Html.DisplayNameFor(model => model.BirthDay)</th>
　：
（省略）
　：
```

こうすれば、後でビューモデルの変数名を変更してもHTMLの変更漏れが無くなりますねネ！(｀・ω・´)


随時追加する…かも？