【问题标题】:ViewModel of Partial View Not Binding to that of main ViewModel after loading via AJAX通过 AJAX 加载后,部分视图的视图模型未绑定到主视图模型的视图模型
【发布时间】:2014-11-17 19:59:53
【问题描述】:

视图模型

//MainViewModel
    public class MainViewModel
    {
        public string UserID { get; set; }
        public string ServiceType { get; set; }
        public List<Service> Services {get; set;}
     }

     public class Service
     {
       public string ServiceID {get; set;}
       public string ServiceName {get; set;
     }

这里是视图

//Index.cshtml
@model ParentViewModel
@{
  <div>
     @Html.DropDownListFor(model => model.UserID, new{onchange="ddSelectionChanged(this)"})
     <div id="services">
        //This is filled with the ajax
     </div>
 </div>
}

这里是 AJAX 调用

<script> 
function ddSelectionChanged(element){
$('#services').load('@(Url.Action("GetServices"))?serviceType =' element.value);
};
</script>

这里是控制器

//Controller
public class UserServicesController : Controller
{
    public ActionResult Index()
    {
      return View();
    }

    public PartialViewResult GetServices(string serviceType)
    {
      //Service servicesViewModel = Fetch Services from DB
      return PartialView("PartialViews/Shared/_Services", servicesViewModel);
    }
}

这工作正常,部分视图按预期呈现,但是当我发布索引时,MainViewModel 的属性“服务”返回“null”。问题是模型的“服务”属性没有重新绑定到 MainViewModel,因为它动态呈现为局部视图。

如何在局部视图渲染时或在 POST 表单之前重新绑定模型?

【问题讨论】:

    标签: asp.net-mvc asp.net-ajax


    【解决方案1】:

    当您发布到操作时,modelbinder 填充的唯一部分是发布的字段和数据。如果您失去了服务,那么您需要在表单中提供字段以在发布期间保留该数据。

    例如,在您的 AJAX 返回的任何 HTML 中,您需要为 Service 上的每个属性添加隐藏输入之类的内容:

    @Html.HiddenFor(m => m.ServiceID)
    @Html.HiddenFor(m => m.ServiceName)
    

    然后,当您的表单发布时,该数据将与其他所有内容一起发布,并且模型绑定器将能够适当地填写您的 Services 列表。但是,您还需要注意生成的字段名称。特别是如果响应 AJAX 请求的操作返回的部分视图直接使用 List&lt;Service&gt; 之类的模型而不是 MainViewModel,则呈现的 HTML 将失去知道它应该绑定到名为 @987654327 的属性的上下文@。换句话说,在您的 HTML 中,您的字段名称最终将类似于 [0].ServiceID 而不是 Services[0].ServiceID。后者对于让模型绑定器正确处理发布的数据是必要的。

    您可以通过将自定义 ViewDataDictionary 传递给您的 Html.PartialHtml.RenderPartial 调用来弥补这一点。通过定义一个新的TemplateInfo 并将HtmlFieldPrefix 设置为所需的任何值,即:

    @Html.Partial("_SomePartial", someModel, new ViewDataDictionary
    {
        TemplateInfo = new System.Web.Mvc.TemplateInfo { HtmlFieldPrefix = "SomePrefix" }
    }
    

    但是,直接返回PartialView 时会比较棘手,因为PartialView 不接受自定义ViewDataDictionary 的参数。我自己没有尝试过,但是您可以直接在您的操作中设置HtmlFieldPrefix

    public PartialViewResult GetServices(string serviceType)
    {
      ViewData.TemplateInfo.HtmlFieldPrefix = "Services";
      //Service servicesViewModel = Fetch Services from DB
      return PartialView("PartialViews/Shared/_Services", servicesViewModel);
    }
    

    另外值得一提的是,如果您以前没有使用过绑定集合,您需要使用for 循环而不是foreach 循环,以便为字段名称提供适当的上下文。例如,如下所示:

    @foreach (var service in Model)
    {
        @Html.HiddenFor(m => service.ServiceID)
    }
    

    将生成名称为service.ServiceID 的字段。 modelbinder 不知道这个值应该去哪里并且基本上会忽略它。相反,您需要这样做:

    @for (var i = 0; i < Model.Count(); i++)
    {
        @Html.HiddenFor(m => m[i].ServiceID)
    }
    

    这会让你的字段名称像[0].ServiceID。同样,前缀是一个问题,因此您需要两个组件才能使其正常工作。

    【讨论】:

    • 嘿,克里斯,谢谢你。你为我节省了几个小时。 ViewData.TemplateInfo.HtmlFieldPrefix = "Services" 这解决了这个问题。干杯,我欠你的人。
    • 您好我有一个类似的问题,即使通过在浏览器中向表中添加新行来正确呈现部分视图,但在表单发布时父模型列表对象的项目数始终为 0。它永远不会进入@for (var i = 0; i
    • 发布一个问题。这永远是你最好的选择。我可能是回答这个问题的人,或者你也可能从其他人那里得到很好的答案。
    • 好的,我将在此处添加链接。谢谢老哥
    • 嗨,克里斯,请看一下,我已经在这里发布了我的问题,stackoverflow.com/questions/47245334/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多