本章では、ASP.NET MVCでSectionを使って、templateをoverrideさせる方法について考えてみました。
HTMLの共通化
レイアウト
ASP.NET MVCでは、HTMLの共通化方法として、「レイアウト」という仕組みがあります。レイアウトは、雛形となるHTMLテンプレートを用意し、テンプレートを継承したHTMLを作成・編集することで、コンテンツに応じたHTMLの実装ができます。
セクション
レイアウトを実現する方法として、「セクション」という機構を利用します。セクションは、雛形HTMLに継承先で編集させるエリアを定義することで利用できます。継承先HTMLでは、雛形HTMLで定義したセクションだけを実装します。
テンプレート(セクション)をオーバーライド
PythonのWebフレームワークである「Django」を確認してみましょう。Djangoには、「テンプレート」という機構があります。これは、テンプレートの定義に加え、合わせて実装も行えます。この機能をASP.NET MVC上で実現するためには、セクションとは違う仕組みを用意する必要があります。
そこで本章では、ASP.NET MVCのセクション機構を変更し、templateをoverrideさせる方法について、解説します。
プログラム
準備
まず、以下のプログラムを事前に準備します。
[SectionExtensions.cs]
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Web; using System.Web.Mvc; using System.Web.WebPages; public static class SectionExtensions { public static HelperResult RedefineSection( this WebPageBase page, string sectionName ) { return RedefineSection(page, sectionName, defaultContents: null); } public static HelperResult RedefineSection( this WebPageBase page, string sectionName, MvcHtmlString helper ) { if (page.IsSectionDefined(sectionName)) { return page.RenderSection(sectionName); } else { return new HelperResult(p => { p.WriteAsync(helper.ToHtmlString()); }); } } public static HelperResult RedefineSection( this WebPageBase page, string sectionName, params Func<dynamic, HelperResult>[] defaultContents ) { if (page.IsSectionDefined(sectionName)) { return page.RenderSection(sectionName); } else if (defaultContents != null) { var builder = new StringBuilder(); foreach (var content in defaultContents) { builder.Append(content(null)); } return new HelperResult(p => { p.WriteLineAsync(builder.ToString()); }); } else { return new HelperResult(p => { }); } } }
本プログラムは、セクション定義時に合わせて初期の表示内容(例.どのページでもjQueryのURLを読み込むなど)を記述できるよう、「RenderSection」を拡張したメソッドになります。
使用例
実際に上述の拡張メソッドを利用してみましょう。まず、親のテンプレートを用意します。
[_MasterLayout.cshtml]
<!DOCTYPE html> <html> <head> @RenderSection("MetaSection", required: false) @RenderSection("TitleSection", required: false) @RenderSection("CssSection", required: false) @RenderSection("HeaderScriptSection", required: false) </head> <body> @RenderSection("HeaderSection", required: false) @RenderSection("BodySection", required: false) @RenderBody() @RenderSection("FooterSection", required: false) @RenderSection("FooterScriptSection", required: false) </body> </html>
次に、子のテンプレートを用意します。ここでは、セクションの定義に加え、継承先で実装がない場合の表示内容を記述しています。もちろん、HTMLのヘルパーメソッドも利用できます。
[_SubLayout.cshtml]
@{ Layout = "~/Views/Shared/_MasterLayout.cshtml"; } @this.RedefineSection("MetaSection", @<meta charset='utf-8' />, @<meta content='telephone=no' name='format-detection' />) @this.RedefineSection("TitleSection", @<title>TEST TITLE</title>) @this.RedefineSection("CssSection", @<link rel="stylesheet" href="@Url.Content("~/Content/main_design.css")" />) @this.RedefineSection("HeaderScriptSection") @this.RedefineSection("HeaderSection", Html.EditorFor(p => p, "HeaderEditorTemplate")) @this.RedefineSection("BodySection", @<label>TEST BODY</label>) @this.RedefineSection("FooterSection") @this.RedefineSection("FooterScriptSection")
そして、孫の実装内容です。ここでは、継承元のセクションと違う内容を記述しています。
[Index.html]
@model TestProject.Models.LoginViewEntity @{ // Login Page Layout = "~/Views/Shared/_SubLayout.cshtml"; } @section HeaderSection { } @section BodySection { @Html.EditorFor(p => p, "LoginEditorTemplate") } @section FooterScriptSection { <script> alert("test"); </script> }
総括
ASP.NET MVCで標準に用意されたテンプレート機能で共通化が大変な場合があったので、今回セクション機構を拡張した仕組みを用意してみました。
以上、「ASP.NET MVC」でテンプレートのオーバーライドを実現する」でした。