![](/img/trans.png)
[英]How to use Razor EditorFor Helper to create a TextBox with a value in from ViewBag
[英]Razor Pages - How to Use EditorFor Templates with Abstract Classes
我已经尝试了一百万零一种不同的东西,并倾注了无数的 SO 解决方案,但我还没有找到一个可行的解决方案。 我有一个页面模型,其中将包含所有从abstract class
派生的不同类的列表。 对于每种类类型,我都有不同的编辑器模板。 因此,当页面加载并且我的项目被加载时,正确的模板会显示出来,一切都如预期的那样。 但是,当我尝试将表单发回时,出现了问题。 我从来没有得到正确的值,我最终得到一个关于尝试提交一个需要抽象类/接口的表单的错误。
为了退后一步并尝试以最简单的形式让思想发挥作用,我们有以下场景:
public class Device
{
public string Type => GetType().FullName;
}
public class Laptop : Device
{
public string CPUIndex { get; set; }
}
public class SmartPhone : Device
{
public string ScreenSize { get; set; }
}
测试页.cshtml
<form method="post">
@Html.EditorFor(m => m.Item, Model.Item.GetType().Name)
<input type="submit" value="Submit"/>
</form>
笔记本电脑编辑器模板
@model Laptop
<p>Laptop</p>
<input asp-for="CPUIndex"/>
TestPage.cs(页面模型)
public Device Item { get; set; }
public void OnGet()
{
Item = new Laptop { CPUIndex = "Random Value" };
}
public void OnPost(Device item)
{
}
这将在我发布表单时引发异常,因为我正在尝试访问一个抽象类。 我在多态绑定下找到了我需要创建自定义绑定的地方。
public class DeviceModelBinder : IModelBinder
{
private Dictionary<Type, (ModelMetadata, IModelBinder)> binders;
public DeviceModelBinder(Dictionary<Type, (ModelMetadata, IModelBinder)> binders)
{
this.binders = binders;
}
public async Task BindModelAsync(ModelBindingContext bindingContext)
{
var modelKindName = ModelNames.CreatePropertyModelName(bindingContext.ModelName, nameof(Device.Type));
var modelTypeValue = bindingContext.ValueProvider.GetValue(modelKindName).FirstValue;
IModelBinder modelBinder;
ModelMetadata modelMetadata;
if (modelTypeValue == "Laptop")
{
(modelMetadata, modelBinder) = binders[typeof(Laptop)];
}
else if (modelTypeValue == "SmartPhone")
{
(modelMetadata, modelBinder) = binders[typeof(SmartPhone)];
}
else
{
bindingContext.Result = ModelBindingResult.Failed();
return;
}
var newBindingContext = DefaultModelBindingContext.CreateBindingContext(
bindingContext.ActionContext,
bindingContext.ValueProvider,
modelMetadata,
bindingInfo: null,
bindingContext.ModelName);
await modelBinder.BindModelAsync(newBindingContext);
bindingContext.Result = newBindingContext.Result;
if (newBindingContext.Result.IsModelSet)
{
// Setting the ValidationState ensures properties on derived types are correctly
bindingContext.ValidationState[newBindingContext.Result] = new ValidationStateEntry
{
Metadata = modelMetadata,
};
}
}
}
public class DeviceModelBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context.Metadata.ModelType != typeof(Device))
{
return null;
}
var subclasses = new[] { typeof(Laptop), typeof(SmartPhone), };
var binders = new Dictionary<Type, (ModelMetadata, IModelBinder)>();
foreach (var type in subclasses)
{
var modelMetadata = context.MetadataProvider.GetMetadataForType(type);
binders[type] = (modelMetadata, context.CreateBinder(modelMetadata));
}
return new DeviceModelBinder(binders);
}
}
然后,我在 Startup.cs 中注册它
services.AddMvc(options =>
{
options.ModelBinderProviders.Insert(0, new DeviceModelBinderProvider());
})
即使这一切,当我提交表单的值item
在onPOST等为null。 我在这里缺少什么?
我找到了。 缺失的部分是我没有在 HTML 中放置一个input
字段来保存Type
属性。
我从此改变了我的TestPage.cshtml
<form method="post">
@Html.EditorFor(m => m.Item, Model.Item.GetType().Name)
<input type="submit" value="Submit"/>
</form>
对此
<form method="post">
@Html.HiddenFor(m => m.Item.Type)
@Html.EditorFor(m => m.Item, Model.Item.GetType().Name)
<input type="submit" value="Submit"/>
</form>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.