連載:ASP.NET MVC入門【バージョン3対応】

第7回 レイアウト/部分ビューでアプリ共通のデザインを定義

山田 祥寛(http://www.wings.msn.to/
2011/09/22
Page1 Page2 Page3 Page4

■RazorのRenderPageメソッドを利用する

 Html.Partial/RenderPartialメソッドの代わりに、Razor標準の構文であるRenderPageメソッドを利用することも可能だ。リスト14の太字部分をRenderPageメソッドで書き換えると、以下のようになる。

@RenderPage("~/Views/Shared/_BookDetail.cshtml")
リスト18 Razorの標準構文であるRenderPageメソッドの利用

 もっとも、RenderPageメソッドはPartial/RenderPartialメソッドのようなビュー検索機能を持たない(「/Views/コントローラ名/ビュー名.cshtml」→「/Views/Shared/ビュー名.cshtml」の順で検索を行わない)。よって、引数にも部分ビューへの正確なパスを渡さなければならず、やや不便だ。著者としては、Html.Partial/RenderPartialメソッドを優先して利用することをお勧めしたい。

 また、RenderPageメソッドとRendePartialメソッドとを同一のページで組み合わせてしまうと、後に呼び出したRenderPartialメソッドの結果が出力されないという不具合もあるようだ(逆なら大丈夫)。両者を同時に呼び出すことはあまりないとは思われるが、注意されたい。

■アクション呼び出しを伴う部分ビューの呼び出し

 もっとも、部分ビューを呼び出すに当たって、何かしら処理を実施したい(=データを準備したい)ということもあるだろう。もちろん、メイン・テンプレートのアクションでデータを準備しても構わないが、同じデータをほかのページでも表示する場合、結局、同じコードをアクションに追加しなければならないため、せっかくビューを共通化した意味がなくなってしまう。

 そこで登場するのが、Html.Actionメソッドだ。Html.Actionメソッドを利用することで、ビュー・テンプレートからアクション・メソッドを呼び出し、その結果を部分ビューで描画できるようになる。これによって、部分ビュー固有の処理をメイン・テンプレートのアクションから明確に切り離せるわけだ。

 さっそく、具体的な例も見てみよう。以下は、デフォルトで用意されているHome/Index.cshtmlもしくはIndex.vbhtmlに、現在時刻表示を埋め込んだサンプルである。


図8 部分ビューを使って、現在時刻を埋め込んだ例

[1]子アクションを準備する

 まずは、Html.Actionメソッドで呼び出すことを想定したアクションCurrentメソッドを、Homeコントローラに追加する。HTTP経由で呼び出される従来のアクションと区別して、このようなアクションのことを「子アクション」とも呼ぶ。子アクションは任意のコントローラに記述することが可能だ(呼び出し側と同じコントローラである必要はない)。

[ChildActionOnly]
public ActionResult Current()
{
  return PartialView("_Current", DateTime.Now);
}
<ChildActionOnly()>
Function Current() As ActionResult
  Return PartialView("_Current", DateTime.Now)
End Function
リスト19 現在時刻を表示するための子アクション(上:HomeController.cs、下:HomeController.vb)

@model System.DateTime

@Model.ToString()
@ModelType System.DateTime

@Model.ToString()
リスト20 現在時刻を表示する部分ビュー(上:_Current.cshtml、下:_Current.vbhtml)

 ビューについては既出の内容であり、あえて解説すべき点はない。ここで注目していただきたいのは、子アクションCurrentメソッドだ。以下に子アクション固有のルールをまとめる。

ChildActionOnly属性を指定する

 ChildActionOnly属性は、そのアクションがHtml.Actionメソッド経由で(子アクションとして)のみ呼び出されることを宣言するものだ。ChildActionOnly属性が指定されていない場合、アクションはHTTP経由で直接呼び出せてしまうので注意してほしい。

 以下は、ChildActionOnly宣言されたアクションを、直接呼び出そうとした場合の結果である。InvalidOperation例外が発生して呼び出せないことが確認できる。


図9 ChildActionOnly宣言されたアクションは直接アクセスできない

部分ビューの呼び出しにはPartialViewメソッドを使う

 子アクションで部分ビューを呼び出すには、いつものViewメソッドではなく、PartialViewメソッドを使わなければならない。Viewメソッドを利用した場合、ビューに_ViewStart.cshtml/_ViewStart.vbhtmlで宣言されたレイアウトが勝手に適用されてしまうためだ。

[2]子アクション呼び出しのコードを追加する

 あとは、子アクションCurrentを呼び出すためのコードを、メイン・テンプレート側に追加するだけだ。

<p>
  ……中略……
  現在時刻:@Html.Action("Current", "Home")
</p>
リスト21 Home#Currentアクションを呼び出すコード(Index.cshtml/Index.vbhtml)

 冒頭述べたように、子アクションを呼び出すには、Html.Actionヘルパーを呼び出すだけだ。Actionメソッドの構文は、以下のとおり。

Action(string action [,string controller] [,object values])
リスト22 Html.Actionメソッドの構文
action:アクション名
controller:コントローラ名
values:ルート・パラメータ

 Actionメソッドは、デフォルトで現在のコントローラに対してアクションを呼び出そうとするため、リスト21の例であれば「@Html.Action("Current")」としても同じ意味だ。

 Actionメソッドの代わりに、RenderActionメソッドを利用することもできる。

 ActionメソッドとRenderActionメソッドとの関係は、ちょうど前述したPartial/RenderPatialメソッドの関係と同様である。Actionメソッドがビューの結果を文字列として返すのに対して、RenderActionメソッドは応答ストリームに直接出力する。よって、リスト21をRenderActionメソッドで表すならば、以下のようになる。

@{ Html.RenderAction("Current", "Home"); }
@Code
  Html.RenderAction("Current", "Home")
End Code
リスト23 Actionメソッドの代わりにRenderActionメソッドを利用

■子アクションでフラグメント・キャッシュを実装する

 Html.Action/RenderActionメソッドを利用すれば、フラグメント・キャッシュ*5の実装も簡単だ。子アクションに対して、以下のようにOutputCache属性を付与するだけでよい。

*5 ページの一部分をキャッシュする仕組み。従来のWebフォームでの実装方法については、拙稿「.NET TIPS:Webフォーム・ページの一部分を断片的にキャッシングするには?」も参照されたい。

[ChildActionOnly]
[OutputCache(Duration = 30)]
public ActionResult Current()
<ChildActionOnly()>
<OutputCache(Duration:=30)>
Function Current() As ActionResult
リスト24 フラグメント・キャッシュを有効化したコード(上:HomeController.cs、下:HomeController.vb)

 フラグメント・キャッシュが有効になっていることを確認するために、Home/Index.cshtml、Index.vbhtmlでも現在時刻を表示するように修正していこう。

現在時刻(親):@DateTime.Now.ToString()<br />
現在時刻(子):@Html.Action("Current")
リスト25 キャッシュの挙動を確認するために修正したコード(Index.cshtml/Index.vbhtml)

 この状態でサンプルを実行した結果が、以下のとおりである。ページをリロードすると、確かに部分ビューで出力した時刻は更新されずに維持されていることが確認できる。



図10 部分ビューによる出力のみがキャッシュされる

■部分ビューで利用できるメソッド

 最後に、部分ビューの描画に関係するメソッド(ヘルパー)をまとめておこう。表2の[アクション]列はアクション・メソッドを間に介せるかどうか、[結果]列は部分ビューの結果を返す方法を表している。

ヘルパー アクション 結果
Html.Partial × 文字列(MvcHtmlString)
Html.RenderPatial × 応答に出力
Html.Action 文字列(MvcHtmlString)
Html.RenderAction 応答に出力
RenderPage × 文字列(HelperResult)
表2 部分ビューに関係するヘルパー

 部分ビューには関係するメソッドも多いので混乱してしまいそうだが、まずは、Partial/Actionメソッドを利用するのを基本としておくと頭を整理しやすいと思う。

 以上、ここまで3回にわたって解説してきたように、Razorではさまざまな仕掛けを用意しており、これらを駆使することで、いかようにもビュー開発を効率化できる。ビューは、アプリケーションの中でもとかく変化の激しい部分でもある。効率化できるところは最大限効率化し、変化に即応できる状態を準備しておくことが大切だ。

 次回、最終回は単体テストについて解説する予定である。最後までよろしくお付き合いいただきたい。End of Article

 

 INDEX
  ASP.NET MVC入門【バージョン3対応】
  第7回 レイアウト/部分ビューでアプリ共通のデザインを定義
    1.ヘッダ/フッタ/メニューを共通化する − レイアウト
    2.レイアウトに複数のコンテンツ領域を設置/レイアウトを入れ子に配置
    3.部分ビューの基本
  4.アクションを伴う部分ビュー呼び出し/子アクションでフラグメント・キャッシュ
 
インデックス・ページヘ  「ASP.NET MVC入門【バージョン3対応】」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間