読者です 読者をやめる 読者になる 読者になる

kikki's tech note

技術ブログです。UnityやSpine、MS製品など技術色々について解説しています。

ASP.NET MVCでテンプレートのオーバーライドを実現する

本章では、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」でテンプレートのオーバーライドを実現する」でした。

※無断転載禁止 Copyright (C) 2015 kikkisnrdec All Rights Reserved.