Dan*_*anP 9 asp.net asp.net-mvc viewmodel
在我的ASP.NET MVC 2 Web应用程序中,我允许用户创建不同数据类型的自定义输入字段以扩展我们的基本输入表单.虽然很棘手,但是从一组自定义字段构建输入表单非常简单.
但是,我现在要处理这个表单的发布,我不确定处理这个问题的最佳方法是什么.通常,我们使用强类型输入模型,它们受到表单上可用的各种静态类型输入的约束.但是,对于如何使用表示不同数据类型的可变数量的输入字段,我感到很茫然.
代表性输入表单可能类似于:
我想过的想法是:
有没有人之前处理过这样的问题?如果是这样,你是如何解决的?
更新:
我的"基础"表单一起处理在另一个输入区域,因此解决方案不需要为此考虑任何类型的继承魔法.我只对处理这个界面上的自定义字段感兴趣,而不是我的"基础"字段.
更新2:
感谢ARM和smartcaveman; 你们俩都为如何做到这一点提供了很好的指导.一旦实施,我将用我的最终解决方案更新这个问题.
这就是我开始处理这个问题的方式。基于 FormKey 属性(可以由索引和/或标签确定,具体取决于),自定义模型绑定器将非常容易构建。
public class CustomFormModel
{
public string FormId { get; set; }
public string Label { get; set; }
public CustomFieldModel[] Fields { get; set; }
}
public class CustomFieldModel
{
public DataType DateType { get; set; } // System.ComponentModel.DataAnnotations
public string FormKey { get; set; }
public string Label { get; set; }
public object Value { get; set; }
}
public class CustomFieldModel<T> : CustomFieldModel
{
public new T Value { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
另外,我注意到下面的评论之一有一个过滤模型活页夹系统。Automapper 的 Jimmy Bogard 在http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/03/17/a-better-model-binder.aspx上发表了一篇关于此方法的非常有用的文章,后来进行了修改,http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/11/19/a-better-model-binder-addendum.aspx。它对我构建自定义模型绑定器非常有帮助。
更新
我意识到我误解了这个问题,并且他专门询问如何处理“具有代表不同数据类型的可变数量的输入字段”的表单的发布。我认为最好的方法是使用与上面类似的结构,但利用复合模式。基本上,您需要创建一个类似的接口IFormComponent,并为要表示的每种数据类型实现它。我编写并评论了一个示例界面,以帮助解释如何实现这一点:
public interface IFormComponent
{
// the id on the html form field. In the case of a composite Id, that doesn't have a corresponding
// field you should still use something consistent, since it will be helpful for model binding
// (For example, a CompositeDateField appearing as the third field in the form should have an id
// something like "frmId_3_date" and its child fields would be "frmId_3_date_day", "frmId_3_date_month",
// and "frmId_3_date_year".
string FieldId { get; }
// the human readable field label
string Label { get; }
// some functionality may require knowledge of the
// Parent component. For example, a DayField with a value of "30"
// would need to ask its Parent, a CompositeDateField
// for its MonthField's value in order to validate
// that the month is not "February"
IFormComponent Parent { get; }
// Gets any child components or null if the
// component is a leaf component (has no children).
IList<IFormComponent> GetChildren();
// For leaf components, this method should accept the AttemptedValue from the value provider
// during Model Binding, and create the appropriate value.
// For composites, the input should be delimited in someway, and this method should parse the
// string to create the child components.
void BindTo(string value);
// This method should parse the Children or Underlying value to the
// default used by your business models. (e.g. a CompositeDateField would
// return a DateTime. You can get type safety by creating a FormComponent<TValue>
// which would help to avoid issues in binding.
object GetValue();
// This method would render the field to the http response stream.
// This makes it easy to render the forms simply by looping through
// the array. Implementations could extend this for using an injected
// formatting
void Render(TextWriter writer);
}
Run Code Online (Sandbox Code Playgroud)
我假设可以通过某种可以作为表单参数包含的 id 来访问自定义表单。有了这个假设,模型绑定器和提供者可能看起来像这样。
public interface IForm : IFormComponent
{
Guid FormId { get; }
void Add(IFormComponent component);
}
public interface IFormRepository
{
IForm GetForm(Guid id);
}
public class CustomFormModelBinder : IModelBinder
{
private readonly IFormRepository _repository;
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
ValueProviderResult result;
if(bindingContext.ValueProvider.TryGetValue("_customFormId", out result))
{
var form = _repository.GetForm(new Guid(result.AttemptedValue));
var fields = form.GetChildren();
// loop through the fields and bind their values
return form;
}
throw new Exception("Form ID not found.");
}
}
Run Code Online (Sandbox Code Playgroud)
显然,这里的所有代码只是为了表达要点,需要完成和清理才能实际使用。此外,即使完成,这也只会绑定到 IForm 接口的实现,而不是强类型业务对象。(将其转换为字典并使用 Castle DictionaryAdapter 构建强类型代理并不是一个巨大的步骤,但由于您的用户在网站上动态创建表单,因此您的解决方案中可能没有强类型模型,并且这是无关紧要的)。希望这有更多帮助。
| 归档时间: |
|
| 查看次数: |
6129 次 |
| 最近记录: |