2009-05-29 186 views
24

這是一個通用設計問題:如何在ASP.NET MVC中實現動態(運行時生成的)表單?ASP.NET MVC中的動態(運行時生成的)表單

這裏的情況:

  1. 甲網站管理員可以定義形式參數(字段,字段的類型,驗證)與GUI(MVC視圖)。
  2. 根據需要,運行時根據管理配置爲最終用戶生成表單。我假設所有這些邏輯都駐留在控制器中 - 或者是擴展方法,動作過濾器或類似的東西。
  3. 最終用戶填寫表格,點擊提交,信息被捕獲到數據庫中。

定製不需要支持嵌套控件,第三方控件等等,但我懷疑一個非常優雅的設計會允許這樣做。大多數情況下,我只需要管理員能夠將其他字段指定爲文本框,複選框,單選按鈕和組合框。我還需要應用程序爲數據分配一個空間以保存在數據庫中,但我相信我已經找到了這一部分。

感謝您的幫助。

+0

我反對結束這個問題。在特定的服務器端框架*上生成運行時生成的表單*的體系結構非常廣泛,但是如下面的答案所證明的,它顯然是可以回答的。所有架構問題都只是因爲架構決策是高層次的,有利有弊嗎? – 2017-08-18 18:11:13

回答

1

這樣做的一種方法是創建您自己的ModelBinder,這將是您生成的表單的核心。 modelbinder負責驗證ModelState並重建類型爲ViewDataModel(假設您的視圖已輸入)。

DataAnnotations模型綁定可能是此通過AttributesViewDataModel一個很好的參考此自定義模型綁定器可以讓你做的是描述屬性的驗證(在UI呈現提示)。但是,這是所有定義的編譯時間,但是可以開始編寫自定義模型綁定器。

在你的情況下,你的模型聯編程序應該從xml文件/字符串獲得運行時字段的驗證。

如果你有這樣一個路徑:

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

然後,你可以找到正確的形式的XML FormsController.Index(string formName),並把它傳遞給視圖。

FormsModel應該包含所有可能的方法來獲取數據我沒有看到任何其他方式。 Xml可以映射到函數名稱(可能是參數),您可以使用FormsModel上的反射來調用ViewData或輸入ViewDataModel與數據。

窗體索引視圖可以通過HtmlHelper擴展生成一個表單,該表單需要XmlDocument

然後,當您(或asp.net mvc)將您的表單綁定到您的ViewData您的自定義模型聯編程序被調用時,它可以檢查當前控制器值以查找formName並查找相應的保存所有驗證規則的xml 。然後ModelBinder負責填寫ModelState任何運行時定義的錯誤。

這是一個艱鉅的任務,但是當在我看來:)

更新一個更好的替代模型數據將是一個非常鬆散的數據庫架構大衛利德爾指出被拉斷成功地值得的。我仍然經歷了將它保存爲xml(或其他序列化格式)的麻煩,並使用它來生成視圖並保存自定義的驗證規則,以便您可以更好地控制每個字段的佈局和驗證。

3

另一種選擇是有一個非常鬆散耦合的數據庫模式。

 
//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 

當您提交通過你的FormCollection產生你的運行視圖(從ApplicationFields)然後就循環和嘗試,並把它放在你需要更新ParentObject。

 
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

我在最近的一個項目同樣需要。我爲此創建了一個類庫。我剛剛發佈了一個新版本的庫。

也許它可以幫助你: ASP.NET MVC Dynamic Forms

+0

只要看看這個相當不錯的解決方案的源代碼,你是否計劃發佈2.0版本的源代碼?如果不是,你有兩個版本之間的差異清單? – Tr1stan 2013-09-20 15:10:28

0

我看不到生成的XForms或任何其他「抽象」在HTML與直線前進一代HTML的比較的巨大的優勢「Web窗體2.0」控件列表適用於型號爲List<Tuple<Meta, Value>>。注意:在服務器端,您在任何情況下都有義務手動解析結果以適應結構。

搜索「下一層的抽象」是良好的快速發展,但看到「生成代碼」(運行時或編譯時)都有自己特定的。通常「生成較低層」的生成代碼比生成「較高層的抽象層」代碼更好。

所以,只要去和在@Foreach循環中生成Web 2控件的wirte代碼即可。

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(例如嵌套集合,自動完成,日期選擇器等)