c# - 从通用 EditorFor 模板绑定模型属性的值
问题描述
我正在尝试编写一个通用框架来帮助开发向导式表单。
我有一个模型,其属性代表向导中的每个步骤,例如
public class ExampleWizardTransaction : WizardTransaction
{
public override TransactionType TransactionType { get; set; } = TransactionType.ExampleWizard;
public override string ControllerName { get; set; } = "WizardExample";
[DisplayName("Client Details")]
public ClientDetails ClientDetails { get; set; } = new ClientDetails();
[DisplayName("Client Questions")]
public ClientQuestions ClientQuestions { get; set; } = new ClientQuestions();
[DisplayName("Client Preferences")]
public ClientPreferences ClientPreferences { get; set; } = new ClientPreferences();
}
[Step(1)]
public class ClientDetails : IStep
{
[Display(Description = "Please enter your first name")]
public string FirstName { get; set; }
[Display(Description = "Please enter your last name")]
public string LastName { get; set; }
}
[Step(2)]
public class ClientQuestions : IStep
{
[DisplayName("What is your favourite car?")]
public string FavouriteCar { get; set; }
[DisplayName("What is your favourite holiday destination?")]
public string FavouriteDestination { get; set; }
}
[Step(3)]
public class ClientPreferences : IStep
{
[DisplayName("Red or Blue?")]
public Colours Colour { get; set; }
[DisplayName("Do you like Indian food")]
public bool LikeFood { get; set; }
}
最初,我对每个向导步骤都有一个部分视图,如下所示:
@model Web.Models.ExampleWizard.Create
<div class="row">
<div class="col-md-6">
@Html.EditorFor(x => x.ExampleWizardTransaction.ClientDetails)
</div>
</div>
使用它,我的表单的值可以正确绑定,因为当我发布它时,MVC 知道绑定上下文。
在我的表单上,我通过传入步骤号来呈现部分视图,例如
Html.RenderPartial($"_CreateWizard_{Model.ExampleWizardTransaction.CurrentStep}", Model);
我正在尝试概括代码,因此我不需要为向导中的每个步骤都包含部分视图。
为此,我正在呈现一个操作,该操作确定与向导步骤关联的类型,并返回一个局部视图:
Html.RenderAction("GetStep", "ExampleWizard", Model.ExampleWizardTransaction);
我的部分视图指定了每个向导步骤实现的接口:
_WizardStep.cshtml
@model Services.ViewModels.Wizard.IStep
<div class="row">
<div class="col-md-6">
@Html.EditorFor(x => x)
</div>
</div>
当我使用上述内容时,表单会正确呈现,但值不再绑定在 POST 上,我认为这是因为它没有属性的绑定上下文(例如,输入类型的 id 和名称不是t 完全合格)。
我的向导步骤中有一个用于字符串属性的 EditorFor 模板,它呈现一个文本框:
@model string
<div class="col-md-12">
<div class="form-group">
<label class="m-b-none" for="@ViewData.Model">
@ViewData.ModelMetadata.DisplayName
</label>
<span class="help-block m-b-none small m-t-none">@ViewData.ModelMetadata.Description</span>
<div class="input-group">
@Html.TextBox("", Model, new {@class = "form-control"})
<div class="input-group-addon">
<i class="fa validation"></i>
</div>
</div>
</div>
</div>
是否可以使用我的通用“_WizardStep.cshtml”部分视图并将当前步骤的属性绑定到我的模型?
我的控制器如下所示:
[HttpPost]
public virtual ActionResult CreateWizard(Create model, string action)
{
var createModel = CreateModel<Create>();
switch (createModel.Save(action))
{
case WizardState.Finished:
return RedirectToActionWithMessage("List", "Transaction", "Completed", ToastNotificationStatus.Success);
case WizardState.Ongoing:
return RedirectToAction(MVC.ExampleWizard.CreateWizard(
model.ExampleWizardTransaction.Id,
model.ExampleWizardTransaction.GetNextStep(action)));
default:
model.MapExistingTransactions<ExampleWizardTransaction>();
return View(model);
}
}
我的“创建”模型包含我的“ExampleWizardTransaction”属性,其中包含实现 IStep 接口的每个向导步骤。
解决方案
从@StephenMuecke 的回答中汲取灵感,我采用了以下方法。
在我的“CreateWizard.cshtml”视图中,我使用以下行呈现该步骤:
@Html.WizardPartialFor(x => x.ExampleWizardTransaction.GetStepObject<IStep>(), "_WizardStep", Model.ExampleWizardTransaction)
这调用了“WizardPartialFor”扩展方法:
public static MvcHtmlString WizardPartialFor<TModel, TProperty>(this HtmlHelper<TModel> helper,
Expression<Func<TModel, TProperty>> expression, string partialViewName, IWizardTransaction wizardTransaction)
{
var compiled = expression.Compile();
var result = compiled.Invoke(helper.ViewData.Model);
PropertyInfo currentStep = wizardTransaction.GetCurrentStepPropertyInfo();
string currentStepName = currentStep.PropertyType.Name;
var name = $"{currentStep.DeclaringType.Name}.{currentStepName}";
var viewData = new ViewDataDictionary(helper.ViewData)
{
TemplateInfo = new TemplateInfo { HtmlFieldPrefix = name }
};
return helper.Partial(partialViewName, result, viewData);
}
public static PropertyInfo GetCurrentStepPropertyInfo(this IWizardTransaction wizardTransaction)
{
var properties = wizardTransaction.GetType().GetProperties()
.Where(x => x.PropertyType.GetCustomAttributes(typeof(StepAttribute), true).Any());
return properties.FirstOrDefault(x => ((StepAttribute)Attribute
.GetCustomAttribute(x.PropertyType, typeof(StepAttribute))).Step == wizardTransaction.CurrentStep);
}
在这个扩展方法中,我们调用一个扩展方法来获取向导步骤对象:
public static TProperty GetStepObject<TProperty>(this IWizardTransaction wizardTransaction)
where TProperty : class
{
var properties = wizardTransaction.GetType().GetProperties()
.Where(x => x.PropertyType.GetCustomAttributes(typeof(StepAttribute), true).Any());
var @object = properties.FirstOrDefault(x => ((StepAttribute)Attribute
.GetCustomAttribute(x.PropertyType, typeof(StepAttribute))).Step == wizardTransaction.CurrentStep);
return @object.GetValue(wizardTransaction) as TProperty;
}
这成功地呈现了我的通用 _WizardStep 部分视图,并成功地绑定了 POST 上的数据。
推荐阅读
- javascript - 未捕获的类型错误:使用 data-SOMETHING 属性将参数传递给 JavaScript 函数时无法读取 null 的属性“样式”
- javascript - 将具有第一行标题的二维数组转换为对象 JavaScript
- java - 使用 Jsoup 删除不在 base64 中的 HTML 中的图像
- r - 将 R 中的列移动到其他三个底部
- python - 有没有办法用python处理矢量图形?(例如 Matplotlib?)
- javascript - 使用 socket.io 将数据从 nodejs 传输到 html
- api - Pinterest 广告 API - 无法检索广告客户帐户下的广告系列
- java - 在像 -Xms512G 这样的大型案例中,JVM 是否会立即获取整个 Xms 空间用于堆?
- google-cloud-platform - 创建有权访问特定 Cloud Storage 存储桶的 IAM 角色/组
- vue.js - 通过已知 id 访问 vuex 数据