第 5 部:編集フォームとテンプレート
MVC ミュージック ストアは、web 開発に MVC と Visual Studio ASP.NET 使用する手順を紹介し、説明するチュートリアル アプリケーションです。
MVC ミュージック ストアは、音楽アルバムをオンラインで販売し、基本的なサイト管理、ユーザー サインイン、ショッピング カート機能を実装する軽量のサンプル ストア実装です。
このチュートリアル シリーズでは、ASP.NET MVC Music Store サンプル アプリケーションをビルドするために実行されるすべての手順について詳しく説明します。 パート 5 では、フォームの編集とテンプレートについて説明します。
過去の章では、データベースからデータを読み込んで表示していました。 この章では、データの編集も有効にします。
StoreManagerController の作成
まず、 StoreManagerController という名前の新しいコントローラーを作成します。 このコントローラーでは、ASP.NET MVC 3 Tools Update で使用できるスキャフォールディング機能を利用します。 次に示すように、[コントローラーの追加] ダイアログのオプションを設定します。
[追加] ボタンをクリックすると、ASP.NET MVC 3 スキャフォールディング メカニズムによって適切な作業が行われることがわかります。
- ローカル Entity Framework 変数を使用して新しい StoreManagerController を作成します
- プロジェクトの Views フォルダーに StoreManager フォルダーを追加します
- アルバム クラスに厳密に型指定された Create.cshtml、Delete.cshtml、Details.cshtml、Edit.cshtml、Index.cshtml ビューが追加されます
新しい StoreManager コントローラー クラスには CRUD (作成、読み取り、更新、削除) コントローラー アクションが含まれています。このアクションは、Album モデル クラスを操作し、データベース アクセスに Entity Framework コンテキストを使用する方法を知っています。
スキャフォールディング ビューの変更
このコードは私たちのために生成されましたが、このチュートリアル全体を通して記述したのと同じように、MVC コード ASP.NET 標準であることを覚えておくことは重要です。 これは、定型コントローラー コードの記述と厳密に型指定されたビューの手動での作成に費やす時間を節約することを目的としていますが、これは、コードを変更してはならない方法に関するコメントに悲惨な警告が付いているような生成されたコードではありません。 これはコードであり、変更する必要があります。
まず、StoreManager インデックス ビュー (/Views/StoreManager/Index.cshtml) を簡単に編集してみましょう。 このビューには、ストア内のアルバムを一覧表示するテーブルが表示され、[編集]、[詳細]、[削除] リンクが表示され、アルバムのパブリック プロパティが含まれます。 この表示ではあまり役に立たないように、AlbumArtUrl フィールドを削除します。 ビュー コードの table セクションで<、次の<強調表示された行で示されているように、AlbumArtUrl 参照を囲む th> 要素と <td> 要素を削除>します。
<table>
<tr>
<th>
Genre
</th>
<th>
Artist
</th>
<th>
Title
</th>
<th>
Price
</th>
<th>
AlbumArtUrl
</th>
<th></th>
</tr>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.Genre.Name)
</td>
<td>
@Html.DisplayFor(modelItem => item.Artist.Name)
</td>
<td>
@Html.DisplayFor(modelItem => item.Title)
</td>
<td>
@Html.DisplayFor(modelItem => item.Price)
</td>
<td>
@Html.DisplayFor(modelItem => item.AlbumArtUrl)
</td>
<td>
@Html.ActionLink("Edit", "Edit", new { id=item.AlbumId }) |
@Html.ActionLink("Details", "Details", new { id=item.AlbumId }) |
@Html.ActionLink("Delete", "Delete", new { id=item.AlbumId })
</td>
</tr>
}
</table>
変更されたビュー コードは次のように表示されます。
@model IEnumerable<MvcMusicStore.Models.Album>
@{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
@Html.ActionLink("Create
New", "Create")
</p>
<table>
<tr>
<th>
Genre
</th>
<th>
Artist
</th>
<th>
Title
</th>
<th>
Price
</th>
<th></th>
</tr>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.Genre.Name)
</td>
<td>
@Html.DisplayFor(modelItem => item.Artist.Name)
</td>
<td>
@Html.DisplayFor(modelItem => item.Title)
</td>
<td>
@Html.DisplayFor(modelItem => item.Price)
</td>
<td>
@Html.ActionLink("Edit", "Edit", new { id=item.AlbumId }) |
@Html.ActionLink("Details", "Details", new { id=item.AlbumId }) |
@Html.ActionLink("Delete", "Delete", new { id=item.AlbumId })
</td>
</tr>
}
</table>
ストア マネージャーを初めて見る
次に、アプリケーションを実行し、/StoreManager/ を参照します。 変更したストア マネージャー インデックスが表示され、ストア内のアルバムの一覧が表示され、[編集]、[詳細]、[削除] へのリンクが表示されます。
[編集] リンクをクリックすると、[ジャンル] と [アーティスト] のドロップダウンなど、アルバムのフィールドを含む編集フォームが表示されます。
下部にある [リストに戻る] リンクをクリックし、アルバムの [詳細] リンクをクリックします。 これにより、個々のアルバムの詳細情報が表示されます。
ここでも、[リストに戻る] リンクをクリックし、[削除] リンクをクリックします。 これにより、確認ダイアログが表示され、アルバムの詳細が表示され、削除するかどうか確認するメッセージが表示されます。
下部にある [削除] ボタンをクリックすると、アルバムが削除され、[インデックス] ページに戻り、削除されたアルバムが表示されます。
ストア マネージャーは完了していませんが、作業コントローラーがあり、CRUD 操作の開始元のコードが表示されます。
ストア マネージャー コントローラーのコードを確認する
ストア マネージャー コントローラーには、適切な量のコードが含まれています。 これを上から下に進みましょう。 コントローラーには、MVC コントローラー用のいくつかの標準名前空間と、Models 名前空間への参照が含まれています。 コントローラーには MusicStoreEntities のプライベート インスタンスがあり、データ アクセスの各コントローラー アクションによって使用されます。
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MvcMusicStore.Models;
namespace MvcMusicStore.Controllers
{
public class StoreManagerController : Controller
{
private MusicStoreEntities db = new MusicStoreEntities();
ストア マネージャーの [インデックス] アクションと [詳細] アクション
インデックス ビューは、Store Browse メソッドで作業するときに前に見たように、各アルバムの参照されるジャンルとアーティストの情報を含むアルバムの一覧を取得します。 [インデックス] ビューは、リンクされたオブジェクトへの参照に従って各アルバムのジャンル名とアーティスト名を表示できるようにしているため、コントローラーは効率的であり、元の要求でこの情報を照会しています。
//
// GET: /StoreManager/
public ViewResult Index()
{
var albums = db.Albums.Include(a => a.Genre).Include(a => a.Artist);
return View(albums.ToList());
}
StoreManager コントローラーの詳細コントローラー アクションは、前に記述した Store Controller Details アクションとまったく同じように機能します。Find() メソッドを使用して Album by ID を照会し、ビューに返します。
//
// GET: /StoreManager/Details/5
public ViewResult Details(int id)
{
Album album = db.Albums.Find(id);
return View(album);
}
アクションの作成メソッド
Create アクション メソッドは、フォーム入力を処理するため、これまでに見てきたメソッドとは少し異なります。 ユーザーが初めて /StoreManager/Create/ にアクセスすると、空のフォームが表示されます。 この HTML ページには、アルバムの詳細を <入力できるドロップダウンとテキスト ボックスの入力要素を含むフォーム> 要素が含まれます。
ユーザーがアルバム フォームの値を入力した後、[保存] ボタンを押して、これらの変更をアプリケーションに送信してデータベース内に保存できます。 ユーザーが [保存] ボタンを押すと、<フォーム>は HTTP-POST を /StoreManager/Create/ URL に戻し、HTTP-POST の一部としてフォーム>値を送信<します。
ASP.NET MVC を使用すると、StoreManagerController クラス内に 2 つの個別の "Create" アクション メソッドを実装できるようにすることで、これら 2 つの URL 呼び出しシナリオのロジックを簡単に分割できます。1 つは 、/StoreManager/Create/ URL への最初の HTTP-GET 参照を処理し、もう 1 つは送信された変更の HTTP-POST を処理します。
ViewBag を使用してビューに情報を渡す
このチュートリアルの前半で ViewBag を使用しましたが、それについてはあまり説明していません。 ViewBag を使用すると、厳密に型指定されたモデル オブジェクトを使用せずに、ビューに情報を渡すことができます。 この場合、HTTP-GET コントローラーの編集アクションでは、ジャンルとアーティストのリストの両方をフォームに渡してドロップダウンを設定する必要があります。これを行う最も簡単な方法は、それらを ViewBag アイテムとして返すことです。
ViewBag は動的オブジェクトです。つまり、これらのプロパティを定義するコードを記述せずに、ViewBag.Foo または ViewBag.YourNameHere を入力できます。 この場合、コントローラー コードでは ViewBag.GenreId と ViewBag.ArtistId を使用するため、フォームで送信されるドロップダウン値は GenreId と ArtistId になります。これは、設定する Album プロパティです。
これらのドロップダウン値は、その目的のために作成された SelectList オブジェクトを使用してフォームに返されます。 これは、次のようなコードを使用して行われます。
ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name");
アクション メソッド コードからわかるように、このオブジェクトの作成には、次の 3 つのパラメーターが使用されています。
- ドロップダウンに表示される項目の一覧。 これは単なる文字列ではなく、ジャンルのリストを渡すことに注意してください。
- SelectList に渡される次のパラメーターは、選択された値です。 これにより、SelectList はリスト内の項目を事前に選択する方法を認識します。 これは、編集フォームを見ると理解しやすくなります。これはかなり似ています。
- 最後のパラメーターは、表示されるプロパティです。 この場合、これは、Genre.Name プロパティがユーザーに表示されることを示しています。
これを念頭に置いて、HTTP-GET Create アクションは非常に単純です。2 つの SelectList が ViewBag に追加され、モデル オブジェクトはフォームに渡されません (まだ作成されていないため)。
//
// GET: /StoreManager/Create
public ActionResult Create()
{
ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name");
ViewBag.ArtistId = new SelectList(db.Artists, "ArtistId", "Name");
return View();
}
ビューの作成にドロップダウンを表示する HTML ヘルパー
ドロップダウン値をビューに渡す方法について説明したので、ビューを簡単に見て、それらの値がどのように表示されるかを確認しましょう。 ビュー コード (/Views/StoreManager/Create.cshtml) では、次の呼び出しが行われ、[ジャンル] ドロップダウンが表示されます。
@Html.DropDownList("GenreId",
String.Empty)
これは HTML ヘルパーと呼ばれ、共通のビュー タスクを実行するユーティリティ メソッドです。 HTML ヘルパーは、ビュー コードを簡潔で読みやすくする上で非常に便利です。 Html.DropDownList ヘルパーは、ASP.NET MVC によって提供されますが、後で説明するように、アプリケーションで再利用するビュー コード用の独自のヘルパーを作成できます。
Html.DropDownList 呼び出しには、表示するリストの取得場所と、事前に選択する必要がある値 (ある場合) の 2 つのことを伝える必要があります。 最初のパラメーター GenreId は、モデルまたは ViewBag で GenreId という名前の値を検索するように DropDownList に指示します。 2 番目のパラメーターは、ドロップダウン リストで最初に選択された値を示すために使用されます。 このフォームは Create フォームであるため、事前に選択する値がなく、String.Empty が渡されます。
転記済みフォームの値の処理
前に説明したように、各フォームには 2 つのアクション メソッドが関連付けられています。 1 つ目は HTTP-GET 要求を処理し、フォームを表示します。 2 つ目は、送信されたフォーム値を含む HTTP-POST 要求を処理します。 コントローラー アクションには [HttpPost] 属性があることに注意してください。これは、HTTP-POST 要求にのみ応答する必要があることを MVC ASP.NET 伝えます。
//
// POST: /StoreManager/Create
[HttpPost]
public ActionResult Create(Album album)
{
if (ModelState.IsValid)
{
db.Albums.Add(album);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name", album.GenreId);
ViewBag.ArtistId = new SelectList(db.Artists, "ArtistId", "Name", album.ArtistId);
return View(album);
}
このアクションには、次の 4 つの責任があります。
-
- フォーム値を読み取る
-
- フォーム値が入力規則に合格しているかどうかを確認する
-
- フォームの送信が有効な場合は、データを保存し、更新された一覧を表示します
-
- フォームの送信が無効な場合は、検証エラーを含むフォームを再表示します
モデル バインドを使用したフォーム値の読み取り
コントローラー アクションは、(ドロップダウン リストから) GenreId と ArtistId の値と Title、Price、AlbumArtUrl のテキスト ボックス値を含むフォーム送信を処理しています。 フォーム値に直接アクセスすることは可能ですが、MVC に組み込まれているモデル バインド機能を使用することをお勧めします ASP.NET。 コントローラー アクションがモデル型をパラメーターとして受け取ると、ASP.NET MVC はフォーム入力 (およびルート値とクエリ文字列値) を使用してその型のオブジェクトを設定しようとします。 これは、名前がモデル オブジェクトのプロパティと一致する値を探すことによって行われます。たとえば、新しい Album オブジェクトの GenreId 値を設定すると、GenreId という名前の入力が検索されます。 ASP.NET MVC で標準メソッドを使用してビューを作成すると、フォームは常に入力フィールド名としてプロパティ名を使用してレンダリングされるため、フィールド名は一致するだけです。
モデルの検証
モデルは、ModelState.IsValid を簡単に呼び出して検証されます。 まだアルバム クラスに検証ルールを追加していません。少しで行います。そのため、今のところ、このチェックにはあまり関係ありません。 重要なのは、この ModelStat.IsValid チェックは、モデルに配置した検証規則に適応するため、検証ルールに対する将来の変更では、コントローラー アクション コードの更新は必要ありません。
送信された値を保存する
フォームの送信が検証に合格した場合は、値をデータベースに保存します。 Entity Framework では、モデルを Albums コレクションに追加し、SaveChanges を呼び出す必要があります。
db.Albums.Add(album);
db.SaveChanges();
Entity Framework によって、値を保持するための適切な SQL コマンドが生成されます。 データを保存した後、更新プログラムを確認できるように、アルバムの一覧にリダイレクトします。 これを行うには、表示するコントローラー アクションの名前を指定して RedirectToAction を返します。 この場合は、Index メソッドです。
検証エラーを含む無効なフォーム送信の表示
無効なフォーム入力の場合、ドロップダウン値が ViewBag に追加され (HTTP-GET の場合と同様)、バインドされたモデル値がビューに返されて表示されます。 検証エラーは、HTML ヘルパーを使用して自動的に @Html.ValidationMessageFor 表示されます。
作成フォームのテスト
これをテストするには、アプリケーションを実行し、/StoreManager/Create/ を参照します。これにより、StoreController Create HTTP-GET メソッドによって返された空白のフォームが表示されます。
いくつかの値を入力し、[作成] ボタンをクリックしてフォームを送信します。
編集の処理
Edit アクション ペア (HTTP-GET と HTTP-POST) は、先ほど見た Create アクション メソッドとよく似ています。 編集シナリオでは既存のアルバムを操作する必要があるため、EDIT HTTP-GET メソッドは、ルート経由で渡される "id" パラメーターに基づいてアルバムを読み込みます。 アルバムを AlbumId で取得するためのこのコードは、[詳細コントローラー] アクションで以前に確認したコードと同じです。 Create/ HTTP-GET メソッドと同様に、ドロップダウン値は ViewBag を介して返されます。 これにより、ViewBag を介して追加のデータ (ジャンルの一覧など) を渡しながら、モデル オブジェクトとして Album をビュー (Album クラスに厳密に入力) に返すことができます。
//
// GET: /StoreManager/Edit/5
public ActionResult Edit(int id)
{
Album album = db.Albums.Find(id);
ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name", album.GenreId);
ViewBag.ArtistId = new SelectList(db.Artists, "ArtistId", "Name", album.ArtistId);
return View(album);
}
[HTTP-POST の編集] アクションは、[HTTP-POST の作成] アクションとよく似ています。 唯一の違いは、dbに新しいアルバムを追加する代わりにということです。Albums コレクションでは、db を使用して Album の現在のインスタンスを見つけている。Entry(album) とその状態を Modified に設定します。 これにより、新しいアルバムを作成するのではなく、既存のアルバムを変更することが Entity Framework に指示されます。
//
// POST: /StoreManager/Edit/5
[HttpPost]
public ActionResult Edit(Album album)
{
if (ModelState.IsValid)
{
db.Entry(album).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name", album.GenreId);
ViewBag.ArtistId = new SelectList(db.Artists, "ArtistId", "Name", album.ArtistId);
return View(album);
}
これをテストするには、アプリケーションを実行し、/StoreManger/ を参照し、アルバムの [編集] リンクをクリックします。
[HTTP-GET の編集] メソッドで表示される [編集] フォームが表示されます。 いくつかの値を入力し、[保存] ボタンをクリックします。
これにより、フォームが投稿され、値が保存され、値が更新されたことを示す [アルバム] リストに戻ります。
削除の処理
削除は、編集と作成と同じパターンに従い、1 つのコントローラー アクションを使用して確認フォームを表示し、もう 1 つのコントローラー アクションを使用してフォームの送信を処理します。
HTTP-GET コントローラーの削除アクションは、以前の Store Manager Details コントローラー アクションとまったく同じです。
//
// GET: /StoreManager/Delete/5
public ActionResult Delete(int id)
{
Album album = db.Albums.Find(id);
return View(album);
}
[ビューの削除] コンテンツ テンプレートを使用して、アルバムの種類に厳密に入力されたフォームを表示します。
[削除] テンプレートにはモデルのすべてのフィールドが表示されますが、大幅に簡略化できます。 /Views/StoreManager/Delete.cshtml のビュー コードを次のように変更します。
@model MvcMusicStore.Models.Album
@{
ViewBag.Title = "Delete";
}
<h2>Delete Confirmation</h2>
<p>Are you sure you want to delete the album titled
<strong>@Model.Title</strong>?
</p>
@using (Html.BeginForm()) {
<p>
<input type="submit" value="Delete" />
</p>
<p>
@Html.ActionLink("Back to
List", "Index")
</p>
}
これにより、削除の確認が簡略化されます。
[削除] ボタンをクリックすると、フォームがサーバーにポストバックされ、DeleteConfirmed アクションが実行されます。
//
// POST: /StoreManager/Delete/5
[HttpPost, ActionName("Delete")]
public ActionResult DeleteConfirmed(int id)
{
Album album = db.Albums.Find(id);
db.Albums.Remove(album);
db.SaveChanges();
return RedirectToAction("Index");
}
HTTP-POST コントローラーの削除アクションは、次のアクションを実行します。
-
- ID でアルバムを読み込む
-
- アルバムを削除し、変更を保存します
-
- アルバムがリストから削除されたことを示すインデックスにリダイレクトします
これをテストするには、アプリケーションを実行し、/StoreManager を参照します。 一覧からアルバムを選択し、[削除] リンクをクリックします。
削除の確認画面が表示されます。
[削除] ボタンをクリックすると、アルバムが削除され、[ストア マネージャー インデックス] ページに戻り、アルバムが削除されたことを示します。
カスタム HTML ヘルパーを使用してテキストを切り捨てる
Store Manager の [インデックス] ページに 1 つの潜在的な問題があります。 アルバム タイトルプロパティとアーティスト名プロパティはどちらも、テーブルの書式設定を無効にするのに十分な長さになります。 カスタム HTML ヘルパーを作成して、ビュー内のこれらのプロパティやその他のプロパティを簡単に切り捨てることができるようにします。
Razor の @helper 構文を使用すると、ビューで使用する独自のヘルパー関数を簡単に作成できます。 /Views/StoreManager/Index.cshtml ビューを開き、 行の直後に次のコードを @model 追加します。
@helper Truncate(string
input, int length)
{
if (input.Length <= length) {
@input
} else {
@input.Substring(0, length)<text>...</text>
}
}
このヘルパー メソッドは、許可する文字列と最大長を受け取ります。 指定されたテキストが指定された長さより短い場合、ヘルパーはそのまま出力します。 長い場合は、テキストが切り捨てられ、"..." がレンダリングされます。剰余の場合は 。
これで、Truncate ヘルパーを使用して、アルバム タイトルプロパティとアーティスト名プロパティの両方が 25 文字未満であることを確認できます。 新しい Truncate ヘルパーを使用した完全なビュー コードを次に示します。
@model IEnumerable<MvcMusicStore.Models.Album>
@helper Truncate(string input, int length)
{
if (input.Length <= length) {
@input
} else {
@input.Substring(0, length)<text>...</text>
}
}
@{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
@Html.ActionLink("Create
New", "Create")
</p>
<table>
<tr>
<th>
Genre
</th>
<th>
Artist
</th>
<th>
Title
</th>
<th>
Price
</th>
<th></th>
</tr>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.Genre.Name)
</td>
<td>
@Truncate(item.Artist.Name, 25)
</td>
<td>
@Truncate(item.Title, 25)
</td>
<td>
@Html.DisplayFor(modelItem => item.Price)
</td>
<td>
@Html.ActionLink("Edit", "Edit", new { id=item.AlbumId }) |
@Html.ActionLink("Details", "Details", new { id=item.AlbumId }) |
@Html.ActionLink("Delete", "Delete", new { id=item.AlbumId })
</td>
</tr>
}
</table>
ここで、/StoreManager/ URL を参照すると、アルバムとタイトルは最大長を下回ります。
注: これは、1 つのビューでヘルパーを作成して使用する簡単なケースを示しています。 サイト全体で使用できるヘルパーの作成の詳細については、ブログ投稿を参照してください。 http://bit.ly/mvc3-helper-options
フィードバック
https://aka.ms/ContentUserFeedback」を参照してください。
以下は間もなく提供いたします。2024 年を通じて、コンテンツのフィードバック メカニズムとして GitHub の issue を段階的に廃止し、新しいフィードバック システムに置き換えます。 詳細については、「フィードバックの送信と表示