好的,所以我终于有了一些我认为正确的工作代码。这是我一起去的。我有两个简单的“实体”;客户和计费客户。它们实际上是在单独的“有界上下文”中,并且出于演示目的,这些类非常简单。
public class Customer
{
public Guid CustomerId { get; set; }
public string Name { get; set; }
}
public class BillingCustomer
{
public Guid CustomerId { get; set; }
public bool IsOverdueForPayment { get; set; }
}
请注意,这两个类都引用了 CustomerId,为了这个演示,它是一个 GUID。
我从一个简单的 HomeController 开始,它构建了一个 ViewModel 将被 Index.cshtml 文件使用:
public ActionResult Index()
{
var customer = new Customer {
CustomerId = Guid.Empty,
Name = "Mike McCarthy" };
var billingCustomer = new BillingCustomer {
CustomerId = Guid.Empty,
IsOverdueForPayment = true };
var compositeViewModel = new CompositeViewModel {
Customer = customer,
BillingCustomer = billingCustomer };
return View(compositeViewModel);
}
CompositeViewModel 类只是一个愚蠢的 DTO,每个域实体都有一个属性,因为我将在我的 Index.cshtml 文件中调用的局部视图每个都需要将它们各自的域模型传递到局部视图中:
public class CompositeViewModel
{
public BillingCustomer BillingCustomer { get; set; }
public Customer Customer { get; set; }
}
这是我生成的 Index.cshtml 文件,它使用 HomeController 上的 Index 方法
@model CompositeViews.ViewModels.CompositeViewModel
<h2>Index - @DateTime.Now.ToString()</h2>
<div id="customerDiv">
@{Html.RenderPartial("_Customer", Model.Customer);}
</div>
<p></p>
<div id="billingCustomerDiv">
@Html.Partial("_BillingCustomer", Model.BillingCustomer)
</div>
这里有几点需要注意:
- 视图正在使用 CompositeViews.ViewModels.CompositeViewModel ViewModel
- Html.RenderPartial 用于渲染每个实体的局部视图,并传入适当的实体。小心 Html.Partial 调用的语法!
所以,这里是 _Customer 部分视图:
@model CompositeViews.Models.Customer
@using (Ajax.BeginForm("Edit", "Customer", new AjaxOptions {
HttpMethod = "POST",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "customerDiv" }))
{
<fieldset>
<legend>Customer</legend>
@Html.HiddenFor(model => model.CustomerId)
<div class="editor-label">
@Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Name)
@Html.ValidationMessageFor(model => model.Name)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
这里的重要部分是 Ajax.BeginForm 调用。请注意,它显式调用了 CustomerController 的 Edit ActionMethod。另请注意,UpdateTargetId 设置为“customerDiv”。这个 div 不在局部视图中,而是在“父”视图 Index.cshtml 中。
下面是 _BillingCustomer 视图
@model CompositeViews.Models.BillingCustomer
@using (Ajax.BeginForm("Edit", "BillingCustomer", new AjaxOptions {
HttpMethod = "POST",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "billingCustomerDiv" }))
{
<fieldset>
<legend>BillingCustomer</legend>
@Html.HiddenFor(model => model.CustomerId)
<div class="editor-label">
@Html.LabelFor(model => model.IsOverdueForPayment)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.IsOverdueForPayment)
@Html.ValidationMessageFor(model => model.IsOverdueForPayment)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
再次注意,UpdateTargetId 设置为 billingCustomerDiv。此 div 位于 Index.cshtml 文件中,而不是此部分视图文件中。
所以,我们唯一还没有看到的是 CustomerController 和 BillingCustomerController 上的 Edit ActionResult。这是客户控制器
public class CustomerController : Controller
{
[HttpGet]
public PartialViewResult Edit(Guid customerId)
{
var model = new Customer {
CustomerId = Guid.Empty,
Name = "Mike McCarthy"};
return PartialView("_Customer", model);
}
[HttpPost]
public ActionResult Edit(Customer customer)
{
return PartialView("_Customer", customer);
}
}
在这个控制器中没有什么真正“发生”,因为帖子直接涉及构建复合 UI。请注意我们如何通过“PartialView”返回并指定要使用的局部视图的名称,以及视图需要呈现的所需模型。
这是 BillingCustomerController
public class BillingCustomerController : Controller
{
[HttpGet]
public PartialViewResult Edit(Guid customerId)
{
var model = new BillingCustomer {
CustomerId = Guid.Empty,
IsOverdueForPayment = true };
return PartialView("_BillingCustomer", model);
}
[HttpPost]
public PartialViewResult Edit(BillingCustomer billingCustomer)
{
return PartialView("_BillingCustomer", billingCustomer);
}
}
同样,与 CustomerController 相同,只是这个控制器正在处理 BillingCustomer 实体。
现在,当我加载 HomeController 的 Index ActionResult 时,我得到一个如下所示的屏幕:
每个保存按钮都会对控制器进行异步回发,部分视图需要更新并与之对话以获取数据,所有这些都不会导致整个页面的定期回发。点击任一保存按钮时,您可以看到 DateTime 戳不会改变。
所以,这就是我如何使用局部视图构建我的第一个复合视图。由于我对 MVC3 还是很陌生,我仍然可能会搞砸一些事情,或者做一些比它需要的更难的事情,但这就是我让它工作的方式。