部分视图的ViewModel在通过AJAX加载后不绑定到主ViewModel的ViewModel

Adi*_*lil 3 asp.net-mvc asp.net-ajax

的ViewModels

//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;
     }
Run Code Online (Sandbox Code Playgroud)

这是视图

//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>
}
Run Code Online (Sandbox Code Playgroud)

这是AJAX Call

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

这是控制器

//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);
    }
}
Run Code Online (Sandbox Code Playgroud)

这工作正常,部分View按预期呈现,但是当我发布索引时,MainViewModel的属性"Services"返回"null".问题是模型的"服务"属性不会重新绑定到MainViewModel,因为它会动态呈现为部分视图.

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

Chr*_*att 6

发布到操作时,模型绑定器填充的唯一部分是已发布的字段和数据.如果您丢失了服务,则需要在表单中提供字段以在帖子中保留该数据.

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

@Html.HiddenFor(m => m.ServiceID)
@Html.HiddenFor(m => m.ServiceName)
Run Code Online (Sandbox Code Playgroud)

然后,当您的表单发布时,该数据将与其他所有内容一起发布,并且模型绑定器将能够Services恰当地填充您的列表.但是,您还需要注意生成的字段名称.特别是如果响应AJAX请求的动作返回的部分视图使用的是List<Service>直接代替的模型MainViewModel,那么渲染的HTML将失去知道它应该绑定到被调用属性的上下文Services.换句话说,在您的HTML中,您的字段名称将最终变为类似[0].ServiceID而不是Services[0].ServiceID.后者对于使模型绑定器正确处理发布的数据是必要的.

您可以通过将自定义传递ViewDataDictionary给您Html.Partial或您的Html.RenderPartial电话来弥补这一点.通过定义一个新的TemplateInfoHtmlFieldPrefix设置为任何它需要的,即:

@Html.Partial("_SomePartial", someModel, new ViewDataDictionary
{
    TemplateInfo = new System.Web.Mvc.TemplateInfo { HtmlFieldPrefix = "SomePrefix" }
}
Run Code Online (Sandbox Code Playgroud)

但是,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);
}
Run Code Online (Sandbox Code Playgroud)

另外值得一提的是,如果您以前没有使用过绑定集合,那么您需要使用for循环而不是foreach循环来为字段名称提供适当的上下文.例如,类似以下内容:

@foreach (var service in Model)
{
    @Html.HiddenFor(m => service.ServiceID)
}
Run Code Online (Sandbox Code Playgroud)

会产生一个名称为的字段service.ServiceID.模型绑定器不知道这个值应该去哪里,基本上会忽略它.相反,你需要做:

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

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