如何正确绑定部分视图,无需发送整个模型

时间:2018-01-18 02:56:57

标签: asp.net-mvc partial-views defaultmodelbinder

我喜欢拍摄我的视图部分并将其分解为多个局部视图,我喜欢只传入部分视图感兴趣的模型部分。通常我喜欢专门为其设置一个模型partial作为传递到主视图的模型的属性。

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

我真的很喜欢这样做因为它使代码更加有条理,更难以让经验不足的程序员来到我的代码,因为一切都非常有条理。到目前为止,这对我来说并不是一个问题,因为我要么不需要从部分输入表单输入,要么使用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; }
}

1 个答案:

答案 0 :(得分:1)

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

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

将绑定到带参数IList<RegistrantModel>的POST方法。要绑定到CourseRegistrationModel,您需要输入

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

有两个选项可以为name属性添加正确的前缀。

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

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

另请参阅getting the values from a nested complex object that is passed to a partial view,了解可用于简化视图中代码的扩展方法

首选方法是对EditorTemplate类型使用RegistrantModel。您需要将部分名称与类名称相同(在您的情况下为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。