如何正确绑定部分视图 w/o 必须发送整个模型

How to bind properly with partial views w/o having to send in entire Model

我喜欢把我的视图分成多个部分视图,我喜欢只传入模型中部分视图感兴趣的部分。一般我喜欢专门放一个模型对于传递到主视图的模型上的部分作为 属性。

问题是我认为这会导致 html 助手无法以模型绑定器可以正确地将其重新组合在一起的方式呈现,因为它在部分中没有意识到它是 属性 另一个对象。

我真的很喜欢这样做,因为它使代码变得更有条理,并且让经验不足的程序员更难来遍历我的代码,因为对他们来说一切都已经非常结构化了。到目前为止,这对我来说还不是问题,因为我要么不需要从 partials 获取表单输入,要么它是通过 ajax 调用处理的。这次我只想使用常规的 DefaultBinder,我想知道是否有一种方法可以在不必将整个模型发送到所有局部视图的情况下完成这项工作?

示例:

主视图中有这行代码:

@{ Html.RenderPartial("_Registrants", Model.Registrants); }

部分注册人如下所示:

@model Models.Order.RegistrantsModel

// stuff...

// important part:
@for(int i = 0; i < Model.Count(); i++)
{
    @Html.HiddenFor(o => o[i].Enabled)
    <ul class="frmRow@(Model[i].Enabled ? "" : " disabled")">
        <li>
            <span class="title">First Name</span>
            @Html.TextBoxFor(o => o[i].FirstName, new { @placeholder = "enter first name" })
            @Html.ValidationMessageFor(o => o[i].FirstName)
        </li>
        <li>
            <span class="title">Last Name</span>
            @Html.TextBoxFor(o => o[i].LastName, new { @placeholder = "enter last name" })
            @Html.ValidationMessageFor(o => o[i].LastName)
        </li>
        <li>
            <span class="title">Email Address</span>
            @Html.TextBoxFor(o => o.First().Email, new { @placeholder = "enter email address" })
            @Html.ValidationMessageFor(o => o[i].Email)
        </li>
    </ul>
}

主模型是这样的:

public class CourseRegistrationModel
{
    public CourseRegistrationModel() { }

    public CourseRegistrationModel(RegistrationItemModel itemModel, PaymentModel paymentModel)
    {
        Item = itemModel;
        Payor = new PayorModel();
        Registrants = new RegistrantsModel();
        Shipping = new ShippingModel();
        Payment = paymentModel;
    }

    public RegistrationItemModel Item { get; set; }
    public PayorModel Payor { get; set; }
    public RegistrantsModel Registrants { get; set; }
    public ShippingModel Shipping { get; set; }
    public PaymentModel Payment { get; set; }
}

这里是 RegistrantsModel 和 RegistrantModel:

public class RegistrantsModel : IEnumerable<RegistrantModel>
{
    public RegistrantsModel()
    {
        _registrants = new List<RegistrantModel>();

        for(int i = 0; i < 5; i++)
            _registrants.Add(new RegistrantModel());

        _registrants.First().Enabled = true; // Show one registrant on form by default
    }

    List<RegistrantModel> _registrants { get; set; }
    public decimal PricePerPerson { get; set; }
    public int NoOfRegistrants { get; set; }

    public RegistrantModel this[int i]
    {
        get { return _registrants[i]; }
    }

    public IEnumerator<RegistrantModel> GetEnumerator() { return _registrants.GetEnumerator(); }
    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return _registrants.GetEnumerator(); }
}

public class RegistrantModel: IEnabled
{
    [RequiredIfEnabled]
    public string FirstName { get; set; }

    [RequiredIfEnabled]
    public string LastName { get; set; }

    [RequiredIfEnabled]
    [EmailAddress(ErrorMessage = "Please Enter a Valid Email Address")]
    public string Email { get; set; }

    public bool Enabled { get; set; }
}

您的部分正在生成具有 name 属性的表单控件,这些属性与 RegistrantModel 的集合相关,例如

<input name="[0].FirstName" ... />

这将绑定到带有参数 IList<RegistrantModel> 的 POST 方法。为了绑定到您的 CourseRegistrationModel,您的输入必须是

<input name="Registrants[0].FirstName" ... />

有 2 个选项可以将正确的前缀添加到 name 属性。

一种是通过在 RenderPartial() 方法中将其作为 AdditionalViewData 传递来添加前缀

@{ Html.RenderPartial("_Registrants", Model.Registrants,
    new ViewDataDictionary { TemplateInfo = new TemplateInfo { HtmlFieldPrefix = "Registrants" }}); }

另请参阅 以了解可用于简化视图中代码的扩展方法

首选方法是对 typeof RegistrantModel 使用 EditorTemplate。您需要将部分命名为与 class 名称相同(在您的情况下为 RegistrantModel.cshtml)并将其放置在 /Views/Shared/EditorTemplates 文件夹中(如果您想要,则在 /Views/YourControllerName/EditorTemplates 中对不同的控制器使用不同的模板)。然后您的模板基于模型的单个实例

@model RegistrantModel

@Html.LabelFor(m => m.FirstName)
@Html.TextBoxFor(m => m.FirstName, new { @placeholder = "enter first name" })
@Html.ValidationMessageFor(m => m.FirstName)
....

并在主视图中使用

@Html.EditorFor(m => m.Registrants)

EditorFor() 方法具有接受单个 TIEnumerable<T> 的重载,并且在集合的情况下,这些方法生成正确的 html集合中的每一项。