如何在MVC3中构建复合UI?

时间:2012-09-28 14:12:18

标签: asp.net-mvc-3 partial-views bounded-contexts

我理解如何使用部分视图,并且在了解如何在视图中设置它们时,我理解Ajax.ActionLink和Ajax.BeginForm。我假设每个局部视图都有自己的控制器。我在这里考虑有限的上下文,因为在每个局部视图中都可以通过自己的控制器与它自己的有界上下文进行对话

我想我遗失的那篇文章是:

  1. 如何将部分视图包含在“主视图”(或保持视图)中,并使每个部分视图独立地发布到单独的控制器操作,然后返回刷新部分视图而不加载“主视图”或持有观点。
  2. “主”视图或保持视图仍然需要有自己的控制器,我想让主控制器不再重新加载其视图,并让主控制器的动作方法产生的视图保持对这两个部分观点。
  3. 我似乎可以采取两种方法,一种是使用“Ajax”。 MVC3的功能,另一个是使用直接jQuery并从客户端手动处理所有这些交互。

    我正在努力做到这两种方式,或者是一种“更适合”这种复合ui结构的方式吗?

    到目前为止,我所看到的唯一的事情是复合ui构造的简单示例,例如通过Ajax.ActionLink链接,刷新页面上的单个,或者写为Ajax.BeginForm的表单,重新填充div部分视图中的内容。

1 个答案:

答案 0 :(得分:4)

好的,所以我终于有了一些工作代码,我认为这是正确的方法。这就是我的用途。我有两个简单的“实体”;客户和BillingCustomer。它们真的意味着处于不同的“有界环境”中,而且这些类对于演示来说非常简单。

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构建一个将由Index.cshtml文件使用的ViewModel:

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>

这里有几点需要注意:

  1. View正在使用CompositeViews.ViewModels.CompositeViewModel ViewModel
  2. Html.RenderPartial用于渲染每个部分视图 实体,并传递适当的实体。小心 这里是Html.Partial调用的语法!
  3. 所以,这是_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上的Edit ActionResult和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);
        }
    }
    

    此控制器中没有任何“真正发生”的事情,因为帖子直接处理构建复合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时,我得到一个如下所示的屏幕:

    CompositeUI

    每个“保存”按钮都会对控制器执行异步回发,部分视图需要更新并与之通信以获取数据,所有这些都不会导致整个页面的常规回发。您可以看到DateTime标记在点击任一保存按钮时不会更改。

    所以,这就是我如何使用局部视图构建我的第一个复合视图。由于我还是MVC3的新手,我仍然可能会搞砸某事,或者以一种比它需要的更难的方式做某事,但这就是我的工作方式。