【问题标题】:Bind ViewModel property from partial view to Main view将 ViewModel 属性从局部视图绑定到主视图
【发布时间】:2016-10-08 17:16:09
【问题描述】:

我对 ASP.NET MVC 以及基本上所有与 Web 相关的东西都很陌生。对不起,如果这是 nooby,但我正在尝试这样做:

我有一个具有复杂属性的 ViewModel(在此处导航):

public class Request
{
    public virtual BaseRequestData RequestData { get; set; }
}

BaseRequestData 是抽象的,但有几个继承自它的类,它们还有其他类似的属性:

public class AcceleratorRequestData : BaseRequestData
{
    [Display(Name="Downside amount")]
    [Range(-100,0,ErrorMessage = "Downside participation must be between 0 and -100")]
    [Required]
    public decimal PutNotional { get; set; }

    [Display(Name="Upside strike")]
    [Range(1, 2, ErrorMessage = "Upside strike must be between 100% and 200%")]
    public decimal CallPercentStrike { get; set; }

}

在我的主“创建”视图上,我绑定到我的请求模型,但我想通过用户从下拉列表中选择它的类型(例如,AcceleratorRequestData)为我的 BaseRequestData 创建一个部分视图。 我尝试过的 是使用一些 jQuery 来调用控制器并根据下拉菜单呈现部分视图。这是我的部分观点之一,它是一堆表单组:

 @model Synapse.Models.AcceleratorRequestData

@Html.ValidationSummary(true,"",new {@class = "text-danger"})
<div class="form-group">
    @Html.LabelFor(m => Model.PutNotional, new {@class = "control-label col-md-2"})
    <div class="col-md-10">
        @Html.EditorFor(model => model.PutNotional, new {htmlAttributes = new {@class = "form-control"}})
        @Html.ValidationMessageFor(model => model.PutNotional, "", new {@class = "text-danger"})
    </div>
</div>
<div class="form-group">
    @Html.LabelFor(m => Model.CallPercentStrike, new { @class = "control-label col-md-2" })
    <div class="col-md-10">
        @Html.EditorFor(model => model.CallPercentStrike, new { htmlAttributes = new { @class = "form-control" } })
        @Html.ValidationMessageFor(model => model.CallPercentStrike, "", new { @class = "text-danger" })
    </div>
</div>

在我的主视图中替换占位符 &lt;div&gt;

@using (Html.BeginHorizontalForm())
{
    @Html.AntiForgeryToken()

    <hr/>
    @Html.ValidationSummary(true, "", new {@class = "text-danger"})
    <div id="accelRequestPh" style="display: none;"></div>

    <input type="submit" value="Create" class="btn btn-default" />

}

但是在这里我的验证属性和绑定失败(显然,因为我的 BaseRequestData 对象未绑定到我的 Request 对象的实例)。我怎样才能做到这一点?我应该使用编辑器模板吗?如果我这样做了,我的主视图模型如何知道它们?

【问题讨论】:

  • 将包含BaseRequestRequest 类作为虚拟属性的目的是什么?我问是因为您不在任何地方使用 Request 对象(您无法验证不存在的内容)。
  • 我需要它来导航。我的父对象是 Request 类型,我关心生成它,每个 Request 都有一个具体的 BaseRequestData 对象。
  • 为抽象定义使用接口并让使用Request 的任何东西使用接口会更好吗?我问是因为您说您的验证中断是因为 BaseRequest 未绑定到 Request,但是您设计此代码的方式很难在不查看整个源代码以及所有内容如何消耗您的依赖链的情况下判断发生了什么(至少对于我是)。
  • 对不起。所以基本上我有一个索引页面,我想要一个“新建”按钮,它将在动态创建的表单中创建一个请求对象,其中的字段取决于下拉列表中选择的 BaseRequestData 类型。我想我现在只做一些简单的事情,让每个下拉链接返回一个具有抽象类型具体的视图。

标签: c# asp.net-mvc entity-framework razor


【解决方案1】:

AcceleratorRequestData 添加到您的Request 类中:

    public class Request
{
    public virtual BaseRequestData RequestData { get; set; }
    public  AcceleratorRequestData acceleratorRequestData { get; set; }
}

然后更改局部视图的模型:

@model Synapse.Models.Request.AcceleratorRequestData

【讨论】:

  • 但这会破坏我的抽象类的目的:(
  • 为什么你认为“它违背了我的抽象类的目的”?它只是将一个类用作其他类的属性。这与您的类继承无关。
  • 是的,但关键是我的 RequestData 会变成它的孩子。否则,我可以像这样手动删除抽象类并手动添加所有子类...
  • 我知道你的意思,但是 MVC 中的模型绑定不能那样工作。您需要根据用户交互在 Request 类中使用不同的类类型作为属性。然后您就可以完全支持数据注释验证和更简单的模型绑定。否则,您需要考虑如何将用户请求绑定到不同的类型。
  • 我设法在这里工作:stackoverflow.com/questions/6484972/…
【解决方案2】:

所以我放弃了渲染不同 html 元素的下拉菜单,而是将下拉菜单移至上一页,以便将具体的子类传递给视图。像这样:

我的下拉菜单调用这个控制器:

    /// <summary>
    /// Get the correct view depending on the dropdown
    /// </summary>
    /// <param name="productType"></param>
    /// <returns></returns>
    [HttpGet]
    public ActionResult Create(string productType)
    {
        Request request = new Request();
        switch (productType)
        {
            case "Accelerator":
                request.RequestData = new AcceleratorRequestData();
                request.ColloquialType = ColloquialType.Accelerator;
                return View(request);
            case "BarrierAccelerator":
                request.RequestData = new BarrierAcceleratorRequestData();
                request.ColloquialType = ColloquialType.BarrierAccelerator;
                return View(request);
            default:
                return RedirectToAction("Index", "Requests");
        }
    }

返回父视图:

@using (Html.BeginHorizontalForm())
{
    @Html.AntiForgeryToken()

    <hr/>
    @Html.ValidationSummary(true, "", new {@class = "text-danger"})
    @Html.EditorFor(x => x.RequestData)

    <input type="submit" value="Create" class="btn btn-default"/>
}

我在这里为我的每个 BaseRequestData 孩子使用编辑器模板。这是一个:

@model Synapse.Models.AcceleratorRequestData

@Html.Hidden("ModelType", Model.GetType())           
@Html.ValidationSummary(true,"",new {@class = "text-danger"})
<div class="form-group">
    @Html.LabelFor(m => Model.PutNotional, new {@class = "control-label col-md-2"})
    <div class="col-md-10">
        @Html.EditorFor(model => model.PutNotional, new {htmlAttributes = new {@class = "form-control"}})
        @Html.ValidationMessageFor(model => model.PutNotional, "", new {@class = "text-danger"})
    </div>
</div>
<div class="form-group">
    @Html.LabelFor(m => Model.CallPercentStrike, new { @class = "control-label col-md-2" })
    <div class="col-md-10">
        @Html.EditorFor(model => model.CallPercentStrike, new { htmlAttributes = new { @class = "form-control" } })
        @Html.ValidationMessageFor(model => model.CallPercentStrike, "", new { @class = "text-danger" })
    </div>
</div>

隐藏的 html 助手来自此链接:ViewModel with List<BaseClass> and editor templates

而且我还必须编辑我的 global.cs

 public class MvcApplication : HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
            log4net.Config.XmlConfigurator.Configure(new FileInfo(Server.MapPath("~/Web.config")));
            ModelBinders.Binders.Add(typeof(BaseRequestData), new BaseRequestDataModelBinder());
        }
    }

    public class BaseRequestDataModelBinder : DefaultModelBinder
    {
        protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
        {
            ValueProviderResult typeValue = bindingContext.ValueProvider.GetValue(bindingContext.ModelName + ".ModelType");
            Type type = Type.GetType(
                (string)typeValue.ConvertTo(typeof(string)),
                true
            );
            object model = Activator.CreateInstance(type);
            bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, type);
            return model;
        }
    }

希望这对某人有所帮助!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-12-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多