Итак, у меня наконец-то есть рабочий код, который я считаю правильным способом сделать это. Вот с чем я пошел. У меня есть две простые "сущности"; Клиент и БиллингКлиент. На самом деле они предназначены для использования в отдельных «ограниченных контекстах», а классы очень просты для демонстрационных целей.
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, который использует метод Index в HomeController.
@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. Обратите внимание, что он явно вызывает метод Edit ActionMethod CustomerController. Также обратите внимание, что для 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, а не в этом файле частичного представления.
Итак, единственное, что мы еще не рассмотрели, — это Edit ActionResult для CustomerController и BillingCustomerController. Вот CustomerController
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);
}
}
В этом контроллере на самом деле ничего не происходит, так как пост касается непосредственно создания составного пользовательского интерфейса. Обратите внимание, как мы возвращаемся через «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, я все еще мог что-то напортачить или сделать что-то сложнее, чем нужно, но именно так я заставил это работать.
person
Michael McCarthy
schedule
01.10.2012