как создать составной пользовательский интерфейс в MVC3?

Я понимаю, как использовать частичные представления, и я понимаю Ajax.ActionLink и Ajax.BeginForm, когда дело доходит до их настройки в представлении. Я предполагаю, что у каждого частичного представления есть собственный контроллер. Я имею в виду ограниченный контекст здесь, так как в каждом частичном представлении можно общаться со своим собственным ограниченным контекстом через свой собственный контроллер.

Я думаю, часть, которую мне не хватает, это:

  1. как включить частичные представления в «главное представление» (или удерживающее представление) и иметь каждое из этих частичных представлений независимой публикацией в отдельное действие контроллера, а затем вернуться, чтобы обновить частичное представление БЕЗ загрузки «основного представления» или удержания представления .
  2. «главное» представление или удерживающее представление по-прежнему должно иметь свой собственный контроллер, я хочу, чтобы главный контроллер не перезагружал свое представление, и пусть представление, созданное методом действия главного контроллера, содержит ссылку на эти два частичных взгляды.

Кажется, есть два подхода, один из которых — использовать «Ajax». функциональность MVC3, другой — использовать прямолинейный jQuery и обрабатывать все это взаимодействие вручную со стороны клиента.

Является ли то, что я пытаюсь сделать, возможным в обоих направлениях, или один из способов «лучше подходит» для этого типа составной конструкции пользовательского интерфейса?

До сих пор единственное, что я видел, — это тривиальные примеры построения составного пользовательского интерфейса, такие как ссылка через Ajax.ActionLink, которая обновляет один на странице, или форма, написанная как Ajax.BeginForm, которая повторно заполняет div некоторым содержимым из частичный вид.


person Michael McCarthy    schedule 28.09.2012    source источник
comment
Да, оба варианта возможны. На самом деле я никогда не пробовал помощники Ajax, я использовал только jQuery ajax.   -  person jrummell    schedule 28.09.2012
comment
Я использую композицию с использованием частичных представлений, чтобы охватить функциональность. Например, контроллер возвращает представление, состоящее из более мелких частей для загрузки контактов и связанных данных, в то время как другое представление обрабатывает данные из контроллера. Это означает, что я могу использовать свои контакты и взгляды и в других областях.   -  person Slicksim    schedule 30.09.2012


Ответы (1)


Итак, у меня наконец-то есть рабочий код, который я считаю правильным способом сделать это. Вот с чем я пошел. У меня есть две простые "сущности"; Клиент и БиллингКлиент. На самом деле они предназначены для использования в отдельных «ограниченных контекстах», а классы очень просты для демонстрационных целей.

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>

Здесь следует отметить пару вещей:

  1. представление использует CompositeViews.ViewModels.CompositeViewModel ViewModel
  2. 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, я получаю экран, который выглядит так:

CompositeUI

Каждая кнопка «Сохранить» будет выполнять асинхронную обратную передачу контроллеру, который необходимо обновить и взаимодействовать с частичным представлением, чтобы получить данные, и все это без регулярной обратной передачи для всей страницы. Вы можете видеть, что штамп DateTime НЕ изменяется при нажатии любой кнопки сохранения.

Итак, вот как я приступил к созданию своего первого составного представления с использованием частичных представлений. Поскольку я все еще очень новичок в MVC3, я все еще мог что-то напортачить или сделать что-то сложнее, чем нужно, но именно так я заставил это работать.

person Michael McCarthy    schedule 01.10.2012