Asp.Net MVCのほとんどの紹介では、テキストやチェックボックスなどの単純な入力フィールドへのモデルのバインドを美しく簡単に整理する方法について説明しています。 あなたが大胆不敵なコーダーがこの段階をマスターし、ドロップダウンリスト、チェックボックスまたはラジオボタンリストを表示する方法を見つけたい場合、
この投稿はあなたのためです。
問題の声明
私たちは名前とジャンルを知っている映画のデータベースを持っています。 リストからジャンルが選択されている映画に関する情報のエディターを表示したいと思います。 問題は、ビューが映画自体とジャンルのリストの両方に関連するデータを持っている必要があることです。 さらに、映画に関するデータは私たちが見せる
ものを指し、ジャンルのリストは情報を
編集する
方法を指します。
最初の方法。 い。
コントローラーを介してフィルムに関するデータを転送することができ、ビューはそれ自体でジャンルのリストを抽出します。 これが考えられるすべての原則の違反であることは明らかですが、理論的にはそのような可能性があります。
このようなモデルのクラスがあります。
public class MovieModel { public string Title { get; set; } public int GenreId { get; set; } } public class GenreModel { public int Id { get; set; } public string Name { get; set; } }
初心者向けガイドのようなコントローラーメソッド:
public ActionResult TheUgly(){ var model = Data.GetMovie(); return View(model); }
ここで、Dataはデータを提供する単なる静的クラスです。 これは議論を簡単にするためだけに考案されたものであり、実際にこのようなものを使用することはお勧めしません。
public static class Data { public static MovieModel GetMovie() { return new MovieModel {Title = "Santa Barbara", GenreId = 1}; } }
ここで、ひどいビュー、またはむしろ、ジャンルのリストに関係するその部分を提供します。 基本的に、コードはすべてのジャンルを引き出して、SelectListItem型の要素に変換する必要があります。
<% var selectList = from genre in Data.GetGenres() select new SelectListItem {Text = genre.Name, Value = genre.Id.ToString()}; %> <%:Html.DropDownListFor(model => model.GenreId, selectList, "choose") %>
ここで何がひどいですか? 私の意見では、Asp.Net MVCの主な利点は、明確な職務分離(SoC)があることです。 特に、ビューでデータを送信するのはコントローラーです。 もちろん、これは教義ではなく、単なる良いルールです。 それに違反すると、提出物に不要なコードの束をひねり、1年で非常に難しいことを整理するリスクがあります。
プラス :シンプルなコントローラー。
マイナス :コントローラーに固有のコードがビューに入ります。 MVCモデルの原則に対する重大な違反。
使用する場合 :デモをすばやくスケッチする必要がある場合。
2番目の方法。 悪い。
前と同様に、コントローラーを介して標準的な方法でモデルを転送します。 ViewDataを介してすべての追加データを送信します。 ここにコントローラーメソッドがあります:
public ActionResult TheBad() { var model = Data.GetMovie(); ViewData["AllGenres"] = from genre in Data.GetGenres() select new SelectListItem {Text = genre.Name, Value = genre.Id.ToString()}; return View(model); }
一般的なケースでは、ViewDataに何でも積み上げることができることは明らかです。 さらに、Viewでこのすべてのビジネスを使用します。
<%:Html.DropDownListFor(model => model.GenreId, (IEnumerable<SelectListItem>) ViewData["AllGenres"], "choose")%>
さらに 、「what」と「how」のデータは明確に分離されています。最初のデータはモデルに格納され、2番目のデータはViewDataに格納されます。
短所 :データは分離されますが、コントローラーメソッドは「オーバーロード」されます。2つの(そして将来-多くの)ものを一度に処理します。 さらに、何らかの理由で、問題を解決する「ハッカー」手段としてのViewDataに対する本能的な態度があります。 動的言語の支持者は、ViewDataを喜んで楽しむかもしれませんが。
使用する場合 :1つまたは2つのリストを持つ小さな形式。
第三の方法。 良い。
必要なすべてのデータを含むモデルを使用します。 本のように。
public class ViewModel { public MovieModel Movie { get; set; } public IEnumerable<SelectListItem> Genres { get; set; } }
コントローラーのタスクは、利用可能なデータからこのモデルを作成することです。
public ActionResult TheGood() { var model = new ViewModel(); model.Movie = Data.GetMovie(); model.Genres = from genre in Data.GetGenres() select new SelectListItem {Text = genre.Name, Value = genre.Id.ToString()}; return View(model); }
プラス :MVCパターンの標準的な実装(これは良いからではなく、他の開発者がトピックに参加しやすいからです)。
短所 :前の例のように、コントローラーメソッドはオーバーロードされています。「何」と「方法」が心配です。 また、同じViewModelクラスで同じ「what」と「how」が接続されています。
使用する場合 :1〜3個のリストと他の非標準入力要素を持つ中小規模のフォームで。
4番目の方法。 トリッキー。
ビューにデータを配信できる別の「バックドア」があります。これはRenderActionメソッドです。 古典によれば、Viewはコントローラーについて知らないはずなので、多くの人がこのメソッドについて不機嫌に笑っています。 私にとって個人的には、これ(神々は私を許してくれます)はWebFormsのUserControlsによく似ています。 つまり、ページの残りの部分から実質的に(このメソッドの呼び出しパラメーターを除いて)独立した要素を作成する機能。
したがって、MovieModelをモデルとして使用し、コントローラーメソッドはTheUglyと同じです。 しかし、新しいコントローラーと、ドロップダウンをレンダリングするためのメソッドと、このドロップダウンを部分的に使用する方法があります。 この部分的な柔軟性を最大限に高めて、他の場合に使用できるように、Dropdown.ascxを呼び出してViews \ Sharedフォルダーに配置します。
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<SelectListItem>>" %> <% =Html.DropDownList(ViewData.ModelMetadata.PropertyName, Model, ViewData.ModelMetadata.NullDisplayText)%>
このビューをレンダリングする方法については、いくつかのトリックがあります。
public class GenreController : Controller{ public ActionResult GetGenresDropdown(int selectedId) { ViewData.Model = from genre in Data.GetGenres() select new SelectListItem { Text = genre.Name, Value = genre.Id.ToString(), Selected = (genre.Id == selectedId) }; ViewData.ModelMetadata = new ModelMetadata( ModelMetadataProviders.Current, null, null, typeof (int), "GenreId") {NullDisplayText = "choose"}; return View("Dropdown"); } }
まず、ここで目的の値の自動選択が失われるため、SelectListItemのSelectedプロパティを手動で設定する必要があります。 次に、重要なメタデータをビューに転送する場合、最初にモデルをインストールしてから、メタデータをインストールする必要があります。 それ以外の場合、モデルに基づいてメタデータが自動的に作成されます。 同じ理由で、戻り値のビュー(モデル)を書くべきではありません。 実際、プロパティの名前とデフォルトのテキスト(NullDisplayText)を決定するには、実際にメタデータが必要です。 ちなみに、後者はなくてもできます。
最後に、コントローラーメソッドはメインビューから呼び出されます。
<% Html.RenderAction("GetGenresDropdown", "Genre", new {selectedId = Model.GenreId}); %>
長所 :コントローラーレベルでの責任の分離:MovieControllerは映画のデータ、GenreControllerはジャンルを担当します。 ビューレベルでは、完全な勝利も得ています。メインビューが大幅に簡素化され、ジャンルの選択の実装の詳細が補助になりました。 ちなみに、長いメソッドの単純化とヘルパーメソッドのコードの一部の削除には、ある程度の類似性があります。
短所 :より多くのコード、より複雑な構造。
使用する場合 :メインビューが十分に大きくなり、ドロップダウンが複数のフィールドに表示される場合、またはジャンルの選択を複数のページで使用する必要がある場合。
5番目の方法。 スマート。
目的の値の入力(または選択)を整理することの重要な部分全体がメインビューから分離されている場合、このメインビューを何らかの形で単純化したいという要望があります。 そして、ここでの明らかな解決策は、Html.EditorForModel()を使用することです。 現在、モデルクラスのメタデータは、特定のフィールドを表示するためのメソッドを選択する役割を果たします。 唯一の問題は、組み込みツールを使用すると、RenderAction()ではなく、適切な場所でRenderPartial()を呼び出すようエンジンに強制できることです。 そのため、対応するRenderActionを呼び出す以外に負荷をかけない部分ビューを作成する必要があります。 (もちろん、フィールドエディターをカスタマイズする必要がある場合は、この特定のビューを変更し、DropDown.ascxをニュートラルのままにします。)
したがって、\ Views \ Movie \ EditorTemplatesフォルダーで、GenreEditor.ascxという部分ビューを作成します。 これには、編集中のジャンルIDプロパティと同じタイプのモデル、つまりintがあります。 ビュー自体には、RenderActionの呼び出しのみが含まれます。
<% Html.RenderAction("GetGenresDropdown", "Genre", new {selectedId = Model}); %>
ビューを使用するには、必要な属性をモデルのジャンルIDプロパティに追加する必要があります。
[UIHint("GenreEditor")]
長所 :前の例と同じですが、同時にメインビューを大幅に簡素化しました。
短所 :追加のビューを作成する必要がありましたが、(これまでのところ)意味のある負荷はかかりません(ただし、おそらく、ヒントを追加するなど、フィールドの編集をカスタマイズできます)。 もう1つの重要なマイナス点は、フォームの一般的な構造をカスタマイズするのが難しいことです(たとえば、左側に1つのフィールドを表示し、右側に残りのフィールドを表示するなど)。 グローバルなカスタマイズが必要な場合(たとえば、どこでもdivの代わりにテーブルを使用する場合)、Object用の独自のテンプレートを作成できます。
使用する場合 :多くのフィールドがある場合、それらは多かれ少なかれ標準的な方法で表示され、多くの場合、フィールドのリストが変更されます。
ここで、カテゴリ、評価などのドロップダウンがある場合、(他のドロップダウンの場合と同様に)Dropdown.aspxを使用できますが、GenreEditorと同様のコントローラーメソッドとパーシャルを記述する必要があります。 これを最適化する方法はありますか? 最初に、基本的なGeneric Controllerを作成し、そこにメソッドを送信できます。 次に、関連付けられたクラスの名前(「ジャンル」)をパーシャルに(たとえば、DataType属性を使用して)何らかの方法で転送し、それに応じてRenderAction呼び出しを構築できます。 これで、GenreEditorの代わりに、ドロップダウンリストから選択するためのユニバーサルエディターができます。 その結果、新しいディレクトリを追加しても、必要なコードの量が増加することはありません。モデルの属性を適宜追加するだけです。 例では、これはそうではありませんが、読者は自分で簡単に実装できます。
他にどのようにできますか?
頭に浮かんだ唯一の方法は、AJAXを使ってドロップダウンを埋めることです。 ここでのプラスは明らかです。データはhtmlから独立して送信され、他の場所で異なる方法で使用できます。 そのようなことはSparkで非常に簡単に実装できると聞きましたが、私の手はまだ試されていません。
そして、なぜ、一般的に、これで蒸気浴をするのですか?
例には常に1つの問題があります。彼らは、彼らが話していることを明確にするのに十分単純でなければなりませんが、そのような難しい決定をする理由は明確ではありません。 それでもこの問題について懸念がある場合は、「いつ使用するか」項目を再度お読みください。
もちろん、これらのソリューションのいずれかを使用する前に、信号対雑音比を推定することをお勧めします。小さなプロジェクトは、コード、クラス、ファイルなどが少ないときに維持しやすく、大きなプロジェクトは扱いやすいです要素の数が最も多く、それぞれが明らかに小さなタスクの解決に焦点を合わせています。 幸いなことに、これらはすべて十分にリファクタリングされています。最初のソリューションの1つから始めて、フォームがより複雑になるにつれて、より高度なオプションに進みます。
他に何? ええ、
ここでソースを入手でき
ます 。 お楽しみください!