これは一般的な設計の質問です:ASP.NET MVCで動的(ランタイム生成)フォームをどのように実装しますか?
状況は次のとおりです。
カスタマイズは、ネストされたコントロールやサードパーティのコントロールなどをサポートする必要はありませんが、非常にエレガントなデザインでそれが可能になると思います。ほとんどの場合、管理者が追加のフィールドをテキストボックス、チェックボックス、ラジオボタン、コンボボックスとして指定できるようにする必要があります。このデータをデータベースに保存するためのスペースを割り当てるアプリケーションも必要になりますが、その部分は理解できていると思います。
助けてくれてありがとう。
最近のプロジェクトでも同じ必要がありました。このためのクラスライブラリを作成しました。ライブラリの新しいバージョンをリリースしました。
多分それはあなたを助けることができます: ASP.NET MVC Dynamic Forms
これは、私の FormFactory ライブラリを使用して非常に簡単に行うことができます。
デフォルトでは、ビューモデルに反映されてPropertyVm[]
配列が生成されますが、プログラムでプロパティを作成することもできるため、データベースから設定を読み込んでからPropertyVm
を作成できます。
これは、Linqpadスクリプトからのスニペットです。
`` `
//import-package FormFactory
//import-package FormFactory.RazorGenerator
void Main()
{
var properties = new[]{
new PropertyVm(typeof(string), "username"){
DisplayName = "Username",
NotOptional = true,
},
new PropertyVm(typeof(string), "password"){
DisplayName = "Password",
NotOptional = true,
GetCustomAttributes = () => new object[]{ new DataTypeAttribute(DataType.Password) }
}
};
var html = FormFactory.RazorEngine.PropertyRenderExtension.Render(properties, new FormFactory.RazorEngine.RazorTemplateHtmlHelper());
Util.RawHtml(html.ToEncodedString()).Dump(); //Renders html for a username and password field.
}
`` `
デモサイト 設定できるさまざまな機能の例(ネストされたコレクション、オートコンプリート、日付ピッカーなど)があります
もう1つのオプションは、非常に疎結合のデータベーススキーマを使用することです。
//これには、管理者ユーザーが設定するすべてのフィールドとタイプが含まれます ** ApplicationFields ** FieldName FieldType .. .. //これらはすべて、ParentObjectID ** FormValues ** ParentObjectID FieldName [.____にマッピングされているすべての値です。 ] FieldValue
ランタイムで生成されたビューを(ApplicationFieldsから)送信するときは、FormCollectionをループして、更新する必要のあるParentObjectに設定してみてください。
public ActionResult MyForm(FormCollection form) { //これはすべてのフィールドを含むメインオブジェクトです var parentObject; foreach(フォーム内の文字列キー) { parentObject.SetValue(key、form [key]); } ...
次に、parentObjectは次のようになります...
public部分クラスParentObject { IList _FormValues; public void SetValue(string key、string value) { //この値がすでに存在するかどうかを確認します FormValue v = _FormValues.SingleOrDefault(k => k.Key == key); // if設定するだけです if(v!= null) { v.Value = value; return; } //そうでない場合、これは追加された新しいフォームフィールドである可能性があるため、新しい値を作成します v = new FormValue { ParentObjectID = this.ID、 Key = key、 Value = value }; _ FormValues.Add(v); } }
これを行う1つの方法は、生成されたフォームの中心となる独自のModelBinder
を作成することです。モデルバインダーは、ModelState
を検証し、入力されたViewDataModel
を再構築します(ビューが入力されていると仮定します)。
DataAnnotations モデルバインダーは、このカスタムモデルバインダーでできることの良いリファレンスになる可能性があります。これは、Attributes
のViewDataModel
を介して、属性の検証を説明します( UIレンダリング)。ただし、これはすべて定義済みのコンパイル時間ですが、カスタムモデルバインダーの作成を開始するための優れたリファレンスになります。
あなたの場合、モデルバインダーは実行時にxmlファイル/文字列からフィールドの検証を取得する必要があります。
次のようなルートがある場合:
_routes.MapRoute(null, "Forms/{formName}/", new { action = "Index", controller = "Forms", formName = ""}),
_
次に、FormsController.Index(string formName)
で正しいフォームxmlを見つけて、それをビューに渡すことができます。
FormsModel
は、これを回避する他の方法では見られないデータを取得するためのすべての可能なメソッドを保持する必要があります。 Xmlは、FormsModel
のリフレクションを使用して呼び出すことができる関数名(場合によっては引数)にマップして、ViewData
を埋めるか、ViewDataModel
と入力してデータを入力できます。
フォームインデックスのビューは、HtmlHelper
をとるXmlDocument
拡張機能を介してそのxmlからフォームを生成できます。
次に、フォームをViewData
にバインドすると(またはasp.net mvc)、カスタムモデルバインダーが呼び出され、現在のコントローラー値を調べてformNameを探し、すべての検証を保持する対応するxmlを調べることができます。ルール。次に、ModelBinder
は、ランタイムで定義されたエラーでModelState
を埋める責任があります。
それは難しい仕事ですが、うまくやってのけると、私の見解ではそれだけの価値があります:)
Updateモデルデータのより良い代替手段は、DavidLiddleが示唆するように非常に緩いデータベーススキーマです。それをxml(または他のシリアル化された形式)として保存し、それを使用してビューを生成し、カスタムModelBinder
の検証ルールを保持して、レイアウトと検証をより細かく制御できるようにするという問題はまだ発生します。各フィールドの。
List<Tuple<Meta, Value>>
のようなモデルの「WebForms2.0」コントロールリストを使用した単純なHTMLの生成と比較して、HTMLよりもXFormsまたはその他の「抽象化」を生成することの大きな利点はわかりません。注:サーバー側では、どのような場合でも、結果を手動で解析して構造に合わせる必要があります。
「次のレイヤーの抽象化」の検索は迅速な開発に適していますが、「コードの生成」(実行時またはビルド時)には固有のものがあります。を参照してください。通常、「下位層」のコードを生成する方が、「上位抽象層」コードを生成するよりも優れたソリューションです。
したがって、@ ForeachループでWeb2コントロールを生成するコードを作成してください。
cottsakの答えは非常に魅力的です。
少なくとも2つのクライアント側XFormsエンジンがあります。これが1つです: