ASP.NET Core MVC - 部分ビューを再帰で呼び出す

部分ビューから自身の部分ビューを再帰で呼び出しできるよね?と思って試したらできましたというお話。

以下は次のul・liを作るサンプルです。

f:id:ichiroku11:20180817221154p:plain

モデルを準備

まずはノードを表すモデルとデータを用意します。

// ノード
public class Node {
    // 名前
    public string Name { get; set; }
    // 子ノード一覧
    public IEnumerable<Node> Children { get; set; } = Enumerable.Empty<Node>();
}

// サンプルデータ
public static class Sample {
    // 適当なデータを作る
    public static IEnumerable<Node> GetNodes() => new[] {
        new Node {
            Name = "A",
            // 子は2つ
            Children = new[] {
                new Node { Name = "A1", },
                new Node { Name = "A2", }
            },
        },
        new Node {
            Name = "B",
            // 子が3つ
            Children = new[] {
                new Node { Name = "B1", },
                new Node {
                    Name = "B2",
                    // 子が2つ
                    Children = new[] {
                        new Node { Name = "B2-1", },
                        new Node { Name = "B2-2", },
                    },
                },
                new Node { Name = "B3", }
            },
        },
        new Node { Name = "C", },
    };
}

partialタグを使って部分ビューを呼び出す

続いてindex.cshtml。

partialタグはHtml.PartialAsyncやHtml.RenderPartialAsyncに相当するタグヘルパーです。ASP.NET Core 2.1から使えるようになりました。

このpartialタグを使って部分ビューを呼び出してレンダリングしています。

<!-- index.cshtml -->
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
   <meta name="viewport" content="width=device-width" />
   <title>Index</title>
</head>
<body>
    // 部分ビューを呼び出す
    <partial name="_List" model="Sample.GetNodes()" />
</body>
</html>

部分ビューから部分ビューを呼び出す

続いて部分ビュー(_List.cshtml)です。自身の部分ビューのレンダリング再帰で呼び出しています。

<!-- _List.cshtml -->
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@model IEnumerable<Node>

<ul>
    @foreach (var node in Model) {
        <li>
            @node.Name
            @if (node.Children.Any()) {
                // 自身の部分ビューを呼び出す(再帰)
                <partial name="_List" model="node.Children" />
            }
        </li>
    }
</ul>

これで想定通りのHTMLが生成されました。