第 3 部:Views と ViewModels
MVC ミュージック ストアは、web 開発に MVC と Visual Studio ASP.NET 使用する手順を紹介し、説明するチュートリアル アプリケーションです。
MVC ミュージック ストアは、音楽アルバムをオンラインで販売し、基本的なサイト管理、ユーザー サインイン、ショッピング カート機能を実装する軽量のサンプル ストア実装です。
このチュートリアル シリーズでは、ASP.NET MVC ミュージック ストア サンプル アプリケーションをビルドするために実行されるすべての手順について詳しく説明します。 パート 3 では、ビューと ViewModel について説明します。
ここまでは、コントローラー アクションから文字列を返しています。 これは、コントローラーのしくみを把握するための優れた方法ですが、実際の Web アプリケーションを構築する方法ではありません。 サイトにアクセスしているブラウザーに HTML を生成し直すより良い方法が必要です。テンプレート ファイルを使用して、HTML コンテンツの返送をより簡単にカスタマイズできます。 ビューの実行内容は、まさにその通りです。
ビュー テンプレートの追加
ビュー テンプレートを使用するには、次のように、HomeController Index メソッドを変更して ActionResult を返し、View() を返します。
public class HomeController : Controller
{
//
// GET: /Home/
public ActionResult Index()
{
return View();
}
}
上記の変更は、文字列を返す代わりに、"ビュー" を使用して結果を返す必要があることを示しています。
次に、適切なビュー テンプレートをプロジェクトに追加します。 これを行うには、Index アクション メソッド内にテキスト カーソルを配置し、右クリックして [ビューの追加] を選択します。 これにより、[ビューの追加] ダイアログが表示されます。
[ビューの追加] ダイアログでは、ビュー テンプレート ファイルをすばやく簡単に生成できます。 既定では、[ビューの追加] ダイアログでは、作成するビュー テンプレートの名前が事前に設定され、それを使用するアクション メソッドと一致します。 HomeController の Index() アクション メソッド内で "ビューの追加" コンテキスト メニューを使用したため、上記の [ビューの追加] ダイアログには、既定で事前設定されたビュー名として "インデックス" があります。 このダイアログのオプションは変更する必要がないため、[追加] ボタンをクリックします。
[追加] ボタンをクリックすると、Visual Web Developer によって新しい Index.cshtml ビュー テンプレートが \Views\Home ディレクトリに作成され、フォルダーがまだ存在しない場合は作成されます。
"Index.cshtml" ファイルの名前とフォルダーの場所は重要であり、MVC の名前付け規則の既定の ASP.NET に従います。 ディレクトリ名 \Views\Home は、コントローラー (HomeController という名前) と一致します。 ビュー テンプレート名 Index は、ビューを表示するコントローラー アクション メソッドと一致します。
ASP.NET MVC を使用すると、この名前付け規則を使用してビューを返すときに、ビュー テンプレートの名前または場所を明示的に指定する必要がなくなります。 HomeController 内で次のようなコードを記述すると、既定で \Views\Home\Index.cshtml ビュー テンプレートがレンダリングされます。
public class HomeController : Controller
{
//
// GET: /Home/
public ActionResult Index()
{
return View();
}
}
Visual Web 開発者は、[ビューの追加] ダイアログで [追加] ボタンをクリックした後、"Index.cshtml" ビュー テンプレートを作成して開きました。 Index.cshtml の内容を次に示します。
@{
ViewBag.Title = "Index";
}
<h2>Index</h2>
このビューは Razor 構文を使用しています。これは、ASP.NET Web Formsおよび以前のバージョンの ASP.NET MVC で使用されるWeb Forms ビュー エンジンよりも簡潔です。 Web Forms ビュー エンジンは、ASP.NET MVC 3 でも引き続き使用できますが、多くの開発者は、Razor ビュー エンジンが MVC 開発 ASP.NET 適していることを確認しています。
最初の 3 行は、ViewBag.Title を使用してページ タイトルを設定します。 このしくみについては、すぐに詳しく説明しますが、まずテキスト見出しのテキストを更新し、ページを表示しましょう。 次に示すように、 <h2> タグを更新して "This is the Home Page" を指定します。
@{
ViewBag.Title = "Index";
}
<h2>This is the Home Page</h2>
アプリケーションを実行すると、新しいテキストがホーム ページに表示されます。
共通サイト要素にレイアウトを使用する
ほとんどの Web サイトには、ナビゲーション、フッター、ロゴ画像、スタイルシート参照など、多くのページ間で共有されるコンテンツがあります。Razor ビュー エンジンを使用すると、/Views/Shared フォルダー内に自動的に作成された _Layout.cshtml というページを使用して、これを簡単に管理できます。
このフォルダーをダブルクリックすると、次に示す内容が表示されます。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>@ViewBag.Title</title>
<link href="@Url.Content("~/Content/Site.css")"
rel="stylesheet" type="text/css" />
<script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")"
type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/modernizr-1.7.min.js")"
type="text/javascript"></script>
</head>
<body>
@RenderBody()
</body>
</html>
個々のビューのコンテンツは コマンドによって @RenderBody() 表示され、外部に表示する一般的なコンテンツは、_Layout.cshtml マークアップに追加できます。 MVC ミュージック ストアには、サイト内のすべてのページの [ホーム] ページと [ストア] 領域へのリンクを含む共通ヘッダーを用意する必要があるため、そのステートメントのすぐ上のテンプレートに @RenderBody() 追加します。
<!DOCTYPE html>
<html>
<head>
<title>@ViewBag.Title</title>
<link href="@Url.Content("~/Content/Site.css")"
rel="stylesheet" type="text/css" />
<script src="@Url.Content("~/Scripts/jquery-1.4.4.min.js")"
type="text/javascript"></script>
</head>
<body>
<div id="header">
<h1>
ASP.NET MVC MUSIC STORE</h1>
<ul id="navlist">
<li class="first"><a href="/"
id="current">Home</a></li>
<li><a
href="/Store/">Store</a></li>
</ul>
</div>
@RenderBody()
</body>
</html>
スタイルシートの更新
空のプロジェクト テンプレートには、検証メッセージを表示するために使用されるスタイルのみを含む、非常に合理化された CSS ファイルが含まれています。 デザイナーは、サイトの外観を定義するために追加の CSS と画像をいくつか提供しました。そこで、それらを追加します。
更新された CSS ファイルとイメージは、 MVC-Music-Store で使用できるMvcMusicStore-Assets.zipの Content ディレクトリに含まれています。 次に示すように、Windows エクスプローラーで両方を選択し、Visual Web Developer のソリューションの [コンテンツ] フォルダーにドロップします。
既存の Site.css ファイルを上書きするかどうかを確認するメッセージが表示されます。 [はい] をクリックします。
これで、アプリケーションの Content フォルダーが次のように表示されます。
次に、アプリケーションを実行し、[ホーム] ページで変更がどのように表示されるかを確認します。
- 変更された内容を確認しましょう。HomeController の Index アクション メソッドは、標準の名前付け規則に従っているため、"return View()" というコードであっても、\Views\Home\Index.cshtmlView テンプレートを見つけて表示しました。
- ホーム ページには、\Views\Home\Index.cshtml ビュー テンプレート内で定義された単純なウェルカム メッセージが表示されます。
- ホーム ページでは、_Layout.cshtml テンプレートが使用されているため、ウェルカム メッセージは標準サイトの HTML レイアウトに含まれています。
モデルを使用してビューに情報を渡す
ハードコーディングされた HTML だけを表示するビュー テンプレートは、非常に興味深い Web サイトを作成しません。 動的 Web サイトを作成するには、代わりにコントローラー アクションからビュー テンプレートに情報を渡す必要があります。
Model-View-Controller パターンでは、Model という用語は、アプリケーション内のデータを表すオブジェクトを指します。 多くの場合、モデル オブジェクトはデータベース内のテーブルに対応していますが、そうする必要はありません。
ActionResult を返すコントローラー アクション メソッドは、モデル オブジェクトをビューに渡すことができます。 これにより、コントローラーは応答の生成に必要なすべての情報をクリーンにパッケージ化し、この情報を View テンプレートに渡して、適切な HTML 応答を生成できます。 これは実際に見ることで理解するのが最も簡単なので、始めましょう。
まず、ストア内のジャンルとアルバムを表すモデル クラスをいくつか作成します。 最初に、Genre クラスを作成します。 プロジェクト内の [Models] フォルダーを右クリックし、[クラスの追加] オプションを選択し、ファイルに "Genre.cs" という名前を付けます。
次に、作成されたクラスにパブリック文字列 Name プロパティを追加します。
public class Genre
{
public string Name { get; set; }
}
注: {get; set; } 表記では、C# の自動実装プロパティ機能が使用されています。 これにより、バッキング フィールドを宣言しなくても、プロパティの利点が得られます。
次に、同じ手順に従って、Title プロパティと Genre プロパティを持つ Album クラス (Album.cs という名前) を作成します。
public class Album
{
public string Title { get; set; }
public Genre Genre { get; set; }
}
これで、モデルからの動的な情報を表示するビューを使用するように StoreController を変更できます。 デモの目的で、要求 ID に基づいてアルバムに名前を付けた場合は、次のビューのようにその情報を表示できます。
まず、[ストアの詳細] アクションを変更して、1 つのアルバムの情報が表示されるようにします。 "using" ステートメントを StoreControllers クラスの先頭に追加して MvcMusicStore.Models 名前空間を含めます。そのため、アルバム クラスを使用するたびに MvcMusicStore.Models.Album と入力する必要はありません。 これで、そのクラスの "usings" セクションが次のように表示されます。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MvcMusicStore.Models;
次に、HomeController の Index メソッドと同様に、文字列ではなく ActionResult を返すように Details コントローラー アクションを更新します。
public ActionResult Details(int id)
これで、ビューに Album オブジェクトを返すようにロジックを変更できます。 このチュートリアルの後半では、データベースからデータを取得しますが、現時点では "ダミー データ" を使用して作業を開始します。
public ActionResult Details(int id)
{
var album = new Album { Title = "Album " + id };
return View(album);
}
注: C# に慣れていない場合は、var を使用すると、アルバム変数が遅延バインドされることを意味すると想定できます。 これは正しくありません。C# コンパイラは、変数に割り当てられているものに基づいて型推論を使用して、アルバムの種類が Album であると判断し、ローカル アルバム変数をアルバム型としてコンパイルしているため、コンパイル時のチェックと Visual Studio コード エディターのサポートが得られます。
次に、アルバムを使用して HTML 応答を生成するビュー テンプレートを作成しましょう。 これを行う前に、新しく作成したアルバム クラスを [ビューの追加] ダイアログで認識できるようにプロジェクトをビルドする必要があります。 [デバッグ]⇨[Build MvcMusicStore]\(MvcMusicStore のビルド\) メニュー項目を選択して、プロジェクトをビルドできます (追加のクレジットの場合は、Ctrl + Shift + B ショートカットを使用してプロジェクトをビルドできます)。
サポート クラスを設定したので、ビュー テンプレートをビルドする準備ができました。 Details メソッド内を右クリックし、[ビューの追加]を選択します。コンテキスト メニューから。
前に HomeController で行ったように、新しいビュー テンプレートを作成します。 StoreController から作成するため、既定では \Views\Store\Index.cshtml ファイルに生成されます。
以前とは異なり、[厳密に型指定されたビューを作成する] チェック ボックスをオンにします。 次に、[View data-class]\(データ クラスの表示\) ドロップダウン リスト内で "Album" クラスを選択します。 これにより、[ビューの追加] ダイアログで、使用する Album オブジェクトが渡されることを想定したビュー テンプレートが作成されます。
[追加] ボタンをクリックすると、次のコードを含む \Views\Store\Details.cshtml ビュー テンプレートが作成されます。
@model MvcMusicStore.Models.Album
@{
ViewBag.Title = "Details";
}
<h2>Details</h2>
最初の行は、このビューがアルバム クラスに厳密に型指定されていることを示します。 Razor ビュー エンジンは、アルバム オブジェクトが渡されたことを認識しているため、モデルのプロパティに簡単にアクセスでき、Visual Web Developer エディターで IntelliSense の利点を得ることもできます。
h2> タグを<更新して、次のように表示されるように行を変更して、アルバムの Title プロパティを表示します。
<h2>Album: @Model.Title</h2>
キーワードの後 @Model にピリオドを入力すると IntelliSense がトリガーされ、Album クラスがサポートするプロパティとメソッドが表示されます。
プロジェクトを再実行し、/Store/Details/5 URL にアクセスしてみましょう。 以下のようなアルバムの詳細が表示されます。
次に、Store Browse アクション メソッドに対して同様の更新を行います。 ActionResult を返すようにメソッドを更新し、新しい Genre オブジェクトを作成して View に返すようにメソッド ロジックを変更します。
public ActionResult Browse(string genre)
{
var genreModel = new Genre { Name = genre };
return View(genreModel);
}
Browse メソッドを右クリックし、[ビューの追加]を選択します。コンテキスト メニューから、厳密に型指定された View を追加し、厳密に型指定された を Genre クラスに追加します。
<ビュー コード (/Views/Store/Browse.cshtml) の h2> 要素を更新して、ジャンル情報を表示します。
@model MvcMusicStore.Models.Genre
@{
ViewBag.Title = "Browse";
}
<h2>Browsing Genre: @Model.Name</h2>
次に、プロジェクトを再実行し、/Store/Browse を参照してみましょう。Genre=Disco URL。 [参照] ページが次のように表示されます。
最後に、 Store Index アクション メソッドを少し複雑に更新し、ストア内のすべてのジャンルの一覧を表示するビューを作成しましょう。 これを行うには、単一のジャンルではなく、モデル オブジェクトとしてジャンルのリストを使用します。
public ActionResult Index()
{
var genres = new List<Genre>
{
new Genre { Name = "Disco"},
new Genre { Name = "Jazz"},
new Genre { Name = "Rock"}
};
return View(genres);
}
Store Index アクション メソッドを右クリックし、以前と同様に [ビューの追加] を選択し、Model クラスとして [ジャンル] を選択し、[追加] ボタンを押します。
最初に、宣言を @model 変更して、ビューに 1 つではなく複数の Genre オブジェクトが必要であることを示します。 /Store/Index.cshtml の最初の行を次のように変更します。
@model IEnumerable<MvcMusicStore.Models.Genre>
これは、複数の Genre オブジェクトを保持できるモデル オブジェクトを操作することを Razor ビュー エンジンに指示します。 リスト<> ジャンルではなく IEnumerable<ジャンル>を使用しているため、後でモデルの種類を IEnumerable インターフェイスをサポートする任意のオブジェクト型に変更できます。
次に、以下の完成したビュー コードに示すように、モデル内の Genre オブジェクトをループ処理します。
@model IEnumerable<MvcMusicStore.Models.Genre>
@{
ViewBag.Title = "Store";
}
<h3>Browse Genres</h3>
<p>
Select from @Model.Count()
genres:</p>
<ul>
@foreach (var genre in Model)
{
<li>@genre.Name</li>
}
</ul>
"@Model" と入力すると、IEnumerable 型のジャンルでサポートされているすべてのメソッドとプロパティが表示されるように、このコードを入力するときに IntelliSense が完全にサポートされていることに注意してください。
"foreach" ループ内では、Visual Web Developer は各項目の種類が Genre であることを認識しているため、ジャンルの種類ごとに IntelliSense が表示されます。
次に、スキャフォールディング機能によって Genre オブジェクトが調べられ、それぞれが Name プロパティを持つことになるので、ループして書き出します。また、個々のアイテムへの [編集]、[詳細]、[削除] リンクも生成されます。 後ほどストア マネージャーでこれを利用しますが、ここでは単純なリストを用意したいと考えます。
アプリケーションを実行して /Store を参照すると、ジャンルの数と一覧の両方が表示されます。
ページ間のリンクの追加
現在、ジャンルを一覧表示する /Store URL には、単にプレーン テキストとしてジャンル名が一覧表示されています。 これを変更して、プレーンテキストではなく、適切な /Store/Browse URL に [ジャンル名] リンクを設定し、"Disco" などの音楽ジャンルをクリックすると /Store/Browse?genre=Disco URL に移動するようにしましょう。 \Views\Store\Index.cshtml ビュー テンプレートを更新して、次のようなコードを使用してこれらのリンクを出力できます (これを入力しないでください。改善します)。
<ul>
@foreach (var genre in Model)
{
<li><a href="/Store/Browse?genre=@genre.Name">@genre.Name</a></li>
}
</ul>
これは機能しますが、ハードコーディングされた文字列に依存しているため、後で問題が発生する可能性があります。 たとえば、コントローラーの名前を変更する場合は、更新する必要があるリンクを探してコードを検索する必要があります。
使用できる別の方法は、HTML ヘルパー メソッドを利用することです。 ASP.NET MVC には、このようなさまざまな一般的なタスクを実行するために、View テンプレート コードから使用できる HTML ヘルパー メソッドが含まれています。 Html.ActionLink() ヘルパー メソッドは特に便利なメソッドであり、HTML <> のリンクを簡単に作成し、URL パスが正しく URL エンコードされていることを確認するなど、面倒な詳細を処理します。
Html.ActionLink() には、リンクに必要な量の情報を指定できるようにするために、いくつかの異なるオーバーロードがあります。 最も簡単なケースでは、クライアントでハイパーリンクがクリックされたときに移動するリンク テキストと Action メソッドのみを指定します。 たとえば、次の呼び出しを使用して、リンク テキスト "Go to the Store Index" を使用して、[ストアの詳細] ページの "/Store/" Index() メソッドにリンクできます。
@Html.ActionLink("Go
to the Store Index", "Index")
注: この場合、現在のビューをレンダリングしているのと同じコントローラー内の別のアクションにリンクしているだけなので、コントローラー名を指定する必要はありませんでした。
ただし、[参照] ページへのリンクにはパラメーターを渡す必要があるため、次の 3 つのパラメーターを受け取る Html.ActionLink メソッドの別のオーバーロードを使用します。
-
- テキストをリンクすると、ジャンル名が表示されます
-
- コントローラー アクション名 (参照)
-
- 名前 (ジャンル) と値 (ジャンル名) の両方を指定するルート パラメーター値
これらすべてをまとめると、これらのリンクをストア インデックス ビューに書き込む方法を次に示します。
<ul>
@foreach (var genre in Model)
{
<li>@Html.ActionLink(genre.Name,
"Browse", new { genre = genre.Name })</li>
}
</ul>
プロジェクトをもう一度実行し、/Store/ URL にアクセスすると、ジャンルの一覧が表示されます。 各ジャンルはハイパーリンクです。クリックすると、/Store/Browse?genre=[genre] URL に移動します。
ジャンル リストの HTML は次のようになります。
<ul>
<li><a href="/Store/Browse?genre=Disco">Disco</a>
</li>
<li><a href="/Store/Browse?genre=Jazz">Jazz</a>
</li>
<li><a href="/Store/Browse?genre=Rock">Rock</a>
</li>
</ul>