web-dev-qa-db-ja.com

ASP.NET MVCの動的(ランタイム生成)フォーム

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

状況は次のとおりです。

  1. サイト管理者は、GUI(MVCビュー)を使用してフォームパラメーター(フィールド、フィールドのタイプ、検証)を定義できます。
  2. 必要に応じて、ランタイムは管理者構成に基づいてエンドユーザー用のフォームを生成します。このロジックはすべてコントローラーにあると思います。あるいは、拡張メソッド、アクションフィルター、またはそのようなものです。
  3. エンドユーザーがフォームに入力し、送信を押すと、情報がデータベースにキャプチャされます。

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

助けてくれてありがとう。

24
Robert Claypool

最近のプロジェクトでも同じ必要がありました。このためのクラスライブラリを作成しました。ライブラリの新しいバージョンをリリースしました。

多分それはあなたを助けることができます: ASP.NET MVC Dynamic Forms

12
Ronnie Overby

これは、私の 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.
}

`` `

デモサイト 設定できるさまざまな機能の例(ネストされたコレクション、オートコンプリート、日付ピッカーなど)があります

4
mcintyre321

もう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); 
} 
} 
3
David

これを行う1つの方法は、生成されたフォームの中心となる独自のModelBinderを作成することです。モデルバインダーは、ModelStateを検証し、入力されたViewDataModelを再構築します(ビューが入力されていると仮定します)。

DataAnnotations モデルバインダーは、このカスタムモデルバインダーでできることの良いリファレンスになる可能性があります。これは、AttributesViewDataModelを介して、属性の検証を説明します( 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の検証ルールを保持して、レイアウトと検証をより細かく制御できるようにするという問題はまだ発生します。各フィールドの。

1
Martijn Laarman

List<Tuple<Meta, Value>>のようなモデルの「WebForms2.0」コントロールリストを使用した単純なHTMLの生成と比較して、HTMLよりもXFormsまたはその他の「抽象化」を生成することの大きな利点はわかりません。注:サーバー側では、どのような場合でも、結果を手動で解析して構造に合わせる必要があります。

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

したがって、@ ForeachループでWeb2コントロールを生成するコードを作成してください。

0

cottsakの答えは非常に魅力的です。

少なくとも2つのクライアント側XFormsエンジンがあります。これが1つです:

https://community.emc.com/community/edn/xmltech

0
John Turnbull