2009-05-29 14 views
24

これは一般的な設計上の質問です。ASP.NET MVCで動的(実行時生成)フォームをどのように実装しますか?ASP.NET MVCの動的(実行時生成)フォーム

  1. サイト管理者は、GUI(MVCビュー)とフォームパラメータ(フィールド、フィールドの種類、検証)を定義することができます。

    ここで状況があります。

  2. 必要に応じて、ランタイムは管理者の設定に基づいてエンドユーザーのフォームを生成します。私は、このロジックのすべてがコントローラにあると仮定しています。あるいは、おそらく拡張メソッド、アクションフィルタ、またはそのようなものです。
  3. エンドユーザーがフォームに記入して送信し、情報がデータベースに取り込まれます。

カスタマイズでは、ネストされたコントロール、サードパーティのコントロールなどをサポートする必要はありませんが、非常に上品なデザインで可能と思われます。ほとんどの場合、追加のフィールドをテキストボックス、チェックボックス、ラジオボタン、コンボボックスとして指定できるように管理者が必要です。また、このデータをdbに保存するための領域を割り当てるアプリケーションが必要になりますが、その部分を把握していると思います。

ありがとうございました。

+0

私はこの質問を閉じることに反対します。特定のサーバー側フレームワーク*上でランタイム生成フォーム*を作成するアーキテクチャーは広範ですが、以下の回答が証明されるにつれて明らかに対応可能です。アーキテクチャーの決定が高いレベルであり、賛否両論があるため、このサイトのすべてのアーキテクチャーに関する質問はトピックから外れていますか? –

答えて

1

生成するフォームの中心になるModelBinderを独自に作成する方法があります。モデルバインダーはModelStateを検証し、タイプがViewDataModel(再入力されたと仮定します)を再構築します。

DataAnnotationsモデルバインダーは、属性の検証を説明(およびUIのレンダリングをほのめかす)あなたのViewDataModelAttributes経由の良いこのカスタムmodelbinderは、あなたがすることができますどのようなこのための参照である可能性があります。しかし、これはすべてコンパイル時に定義されていますが、カスタムモデルバインダーの作成を開始するには参考になります。

モデルバインダーは、実行時にXMLファイル/文字列からフィールドの検証を取得する必要があります。

あなたのようなルートがある場合:

routes.MapRoute(null, "Forms/{formName}/", new { action = "Index", controller = "Forms", formName = ""}), 

が次にあなたがFormsController.Index(string formName)で正しいフォームXMLを見つけて、ビューに渡すことができます。

FormsModelは、データを取得するためのすべての可能な方法を保持する必要があります。これ以外の方法はありません。 Xmlは、FormsModelの反射を使用してViewDataを入力するか、ViewDataModelとデータを入力して呼び出すことができる関数名(場合によっては引数)にマップできます。

フォームインデックスのビューでは、を取るHtmlHelper拡張子を介してそのXMLからフォームを生成できます。

あなたのフォームをViewDataにバインドすると、現在のコントローラの値が検査され、formNameが検索され、すべての検証ルールが格納されている対応するxmlが検索されます。 ModelBinderは、実行時に定義されたエラーでModelStateを埋め尽くします。

それは大変な作業だが、私の見解では、それも価値が正常に引っ張ったときに:)

更新をデビッド・リドルが示唆するように、データをモデル化するためのより良い代替は非常に緩いデータベーススキーマになります。私はまだxml(または他のシリアライズされた書式)として保存し、ビューを生成し、カスタムModelBinderの検証規則を保持するためにそれを使用して、各フィールドのレイアウトと検証をより詳細に制御するという問題を解決します。

3

もう1つの方法は、非常に疎結合のデータベーススキーマを作成することです。

あなたはFormCollectionてそしてちょうどループ(ApplicationFieldsから)ビュー生成ランタイムを提出し、試してみて、あなたが更新する必要がParentObjectにそれを設定し
 
//this will contain all the fields and types that the admin user sets 
**ApplicationFields** 
FieldName 
FieldType 
... 

//these are all the values that have some mapping to a ParentObjectID 
**FormValues** 
ParentObjectID 
FieldName 
FieldValue 

 
public ActionResult MyForm(FormCollection form) 
{ 
    //this is the main object that contains all the fields 
    var parentObject; 

    foreach (string key in form) 
    { 
     parentObject.SetValue(key, form[key]); 
    } 
    ... 

次に、あなたのparentObjectはこのようなものかもしれません...

 
public partial class ParentObject 
{ 
    IList _FormValues; 

    public void SetValue(string key, string value) 
    { 
     //try and find if this value already exists 
     FormValue v = _FormValues.SingleOrDefault(k => k.Key == key); 

     //if it does just set it 
     if (v != null) 
     { 
      v.Value = value; 
      return; 
     } 

     //else this might be a new form field added and therefore create a new value 
     v = new FormValue 
     { 
      ParentObjectID = this.ID, 
      Key = key, 
      Value = value 
     }; 

     _FormValues.Add(v); 
    } 
} 
12

私は最近のプロジェクトで同じニーズを持っていました。私はこれのためのクラスライブラリを作成しました。私はちょうどライブラリの新しいバージョンをリリースしました。

多分それはあなたを助けることができる:私はHTMLのまっすぐ前方世代と比較してHTML上のXFormsや他の「抽象化」を生成するの巨大な利点を見ることができない ASP.NET MVC Dynamic Forms

+0

この素晴らしいソリューションのソースコードを見るだけで、バージョン2.0のソースコードをリリースする予定ですか?そうでない場合、2つのバージョンの違いのリストがありますか? – Tr1stan

+0

ソースが解放されました。プロジェクト全体がオープンソースの@ codeplexです。 –

+0

ああ、v2のリリース日が2012なので混乱していましたが、v1をリリースした2010年以降、ソースコードは変更されていません。ありがとう。 – Tr1stan

0

コントロールリスト「のWeb 2.0フォーム」 List<Tuple<Meta, Value>>のようなモデルの場合注:サーバー側では、結果を構文解析に合わせて解析する必要があります。

「次のレイヤの抽象化」を検索すると、迅速な開発に適していますが、「コード生成」(実行時またはビルド時)には固有の固有のコードがあります。通常、「下位レイヤー」の生成コードは、「上位抽象レイヤー」コードを生成するよりも優れたソリューションです。

@ForeachループでWeb 2コントロールを生成するコードを作成してください。

4

私の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. 
} 

`

あなたが(例えばを設定することができ、様々な特徴の例とdemo siteをTheresのオートコンプリート、日付ピッカーなど)