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

时间:2014-11-17 19:59:53

标签: 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;
     }

这是视图

//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 Call

<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);
    }
}

这很好,部分View按预期呈现,但是当我发布索引时,MainViewModel的属性“Services”返回“null”。问题是模型的“服务”属性不会重新绑定到MainViewModel,因为它会动态呈现为局部视图。

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

1 个答案:

答案 0 :(得分:6)

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

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

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

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

您可以通过将自定义Html.Partial传递到Html.RenderPartialTemplateInfo来电来弥补此问题。通过定义一个新的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

会导致名称为@for (var i = 0; i < Model.Count(); i++) { @Html.HiddenFor(m => m[i].ServiceID) } 的字段。模型绑定器不知道该值应该去哪里,基本上会忽略它。相反,你需要这样做:

[0].ServiceID

这样可以获得名称为{{1}}的字段。同样,前缀是一个问题,因此您需要两个组件才能使其正常工作。