【问题标题】:Custom EditorTemplate not returning Form values Correctly自定义 EditorTemplate 未正确返回表单值
【发布时间】:2016-02-19 09:42:43
【问题描述】:

我有一个具有复杂类型作为属性的模型。我为复杂的子类型创建了一个自定义 DisplayEditor,并且在加载页面时它被正确绑定。在进行编辑后发布页面时,Dependents 类型被设置为 null。下面是代表子 Dependents 属性的 Employee 模型的代码:

[Display(Name = "Dependents")]
[DataType(DataType.MultilineText)]
public List<Dependent> Dependents { get; set; }

这里是依赖模型:

[Serializable]
public class Dependent : Person
{
    public Dependent()
    {
        Deduction deduction = new Deduction(this) { Amount = Constants.DependentDeductionAmount };
        this.Deduction = deduction;
    }

    [Key]
    [HiddenInput(DisplayValue = false)]
    public int DependentId { get; set; }

    [Required]
    [Display(Name = "Dependent Type")]
    public DependentType DependentType { get; set; }

    [Required]
    public override double DeductionAmount => Constants.DependentDeductionAmount;
}

员工控制器上的 2 个编辑操作方法(我尝试过 TryUpdateModel,不起作用):

    public ViewResult Edit(int employeeId)
    {
        if (employeeId < 0) throw new ArgumentOutOfRangeException(nameof(employeeId));

        Employee employee = _employeeRepository.Employees.FirstOrDefault(e => e.EmployeeId == employeeId);

        bool result = TryUpdateModel(employee, new FormValueProvider(ControllerContext));

        return View(employee);
    }

    [HttpPost]
    public ActionResult Edit(Employee employee)
    {
        if (employee == null) throw new ArgumentNullException(nameof(employee));

        if (ModelState.IsValid)
        {
            employee.Changed = true;
            employee.Dependents.ForEach(d => d.Changed = true);
            _employeeRepository.SaveEmployee(employee);
            TempData["message"] = $"{employee} has been saved.";
            return RedirectToAction("Index");
        }
        else {
            // there is something wrong with the data values
            return View(employee);
        }
    }

这里是 Edit.cshtml:

@model Paylocity.HR.Domain.Entities.Employee

@{
    ViewBag.Title = $"{"Edit"} {Model}";
}

<div class="panel panel-default">
    <div class="panel-heading">
        <h3>@ViewBag.Title</h3>
</div>
@using (Html.BeginForm("Edit", "Employee"))
{
    @Html.AntiForgeryToken()

    <div class="form-horizontal">
        <hr/>
        @Html.ValidationSummary(true, "", new {@class = "text-danger"})
        <h4>Employee</h4>
        <div class="form-group">
            @Html.LabelFor(model => model.FirstName, htmlAttributes: new {@class = "control-label col-md-2"})
            <div class="col-md-10">
                @Html.EditorFor(model => model.FirstName, new {htmlAttributes = new {@class = "form-control"}})
                @Html.ValidationMessageFor(model => model.FirstName, "", new {@class = "text-danger"})
            </div>
        </div>
        <div class="form-group">
            @Html.LabelFor(model => model.LastName, htmlAttributes: new {@class = "control-label col-md-2"})
            <div class="col-md-10">
                @Html.EditorFor(model => model.LastName, new {htmlAttributes = new {@class = "form-control"}})
                @Html.ValidationMessageFor(model => model.LastName, "", new {@class = "text-danger"})
            </div>
        </div>
        <hr/>
        @Html.EditorFor(model => model.Dependents, "Dependents")   
        @Html.HiddenFor(model => model.EmployeeId)
    </div>
    <div class="panel-footer">
        <input type="submit" value="Save" class="btn btn-primary"/>
        @Html.ActionLink("Cancel and return to List", "Index", null, new {@class = "btn btn-default"})
    </div>
}
</div>

这里是 Dependent.cshtml EditorTemplate:

@model IEnumerable<Dependent>
@using Paylocity.HR.Domain.Entities

@foreach (var dep in Model)
{
<h4>Dependent</h4>
<div class="form-group">
    @Html.LabelFor(m => dep.FirstName, htmlAttributes: new { @class = "control-label col-md-2" })
    <div class="col-md-10">
        @Html.EditorFor(m => dep.FirstName, new { htmlAttributes = new { @class = "form-control" } })
        @Html.ValidationMessageFor(m => dep.FirstName, "", new { @class = "text-danger" })
    </div>
</div>
<div class="form-group">
    @Html.LabelFor(m => dep.LastName, htmlAttributes: new { @class = "control-label col-md-2" })
    <div class="col-md-10">
        @Html.EditorFor(m => dep.LastName, new { htmlAttributes = new { @class = "form-control" } })
        @Html.ValidationMessageFor(model => dep.LastName, "", new { @class = "text-danger" })
    </div>
</div>
<div class="form-group">
    @Html.LabelFor(m => dep.DependentType, htmlAttributes: new { @class = "control-label col-md-2" })
    <div class="col-md-10">
        @Html.EnumDropDownListFor(m => dep.DependentType, new { @class = "form-control" })
        @Html.ValidationMessageFor(m => dep.DependentType, "", new { @class = "text-danger" })
    </div>
</div>
<hr />
}

employee 对象绑定正确并且是可更新的,它只是没有正确绑定的依赖子类型。 HTML 为从属表单字段显示正确的 ID/名称(我相信?)。我是否需要实现某种自定义活页夹代码,还是我在这里遗漏了一些明显的东西?

这是我关于 SO 的第一个问题,希望我提供了足够的信息。

【问题讨论】:

    标签: c# .net asp.net-mvc asp.net-mvc-5


    【解决方案1】:

    Dependent.cshtml 模板中的模型更改为@model Dependent(不能是IEnumerable&lt;T&gt;)并删除foreach 循环,该循环生成与您的模型无关的name 属性(并重复@987654326 @attributes 是无效的 html)

    它还需要位于/Views/Shared/EditorTemplates//Views/yourControllerName/EditorTemplates/ 文件夹中

    @model Dependent
    ...
    @Html.EditorFor(m => m.FirstName, new { htmlAttributes = new { @class = "form-control" } })
    ...
    @Html.EditorFor(m => m.LastName, new { htmlAttributes = new { @class = "form-control" } })
    

    等等。然后在主视图中,使用

    @Html.EditorFor(model => model.Dependents)
    

    EditorFor() 方法接受 IEnumerable&lt;T&gt; 并将为集合中的每个项目生成正确的 html,包括带有索引器的正确 name 属性

    【讨论】:

    • 我以为我已经尝试过这种方法(将 EditorTemplate 更改为不使用集合,并直接绑定到主视图中的集合),但显然我没有,或者错过了其中的一些内容过程。这就是答案,我知道这很简单。谢谢!
    【解决方案2】:

    我认为您的问题是您生成项目列表的方式。

    使用索引而不是 foreach。

    因此你的 Dependent.cshtml EditorTemplate

    做一些类似的事情:

    @for(int i = 0; i < Model.Count(); i++)
    {
    <h4>Dependent</h4>
    <div class="form-group">
        @Html.LabelFor(m => Model[i].FirstName, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.EditorFor(m => Model[i].FirstName, new { htmlAttributes = new { @class = "form-control" } })
            @Html.ValidationMessageFor(m => Model[i].FirstName, "", new { @class = "text-danger" })
        </div>
    </div>
    
    // rest field follow same pattern
    }
    

    有关绑定列表的更多信息,请查看此帖子 http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/

    【讨论】:

    • 这也可以。赞成,但将斯蒂芬的答案标记为 b/c 它更干净。不过谢谢!
    • 实际上这行不通。模型必须是 IList&lt;T&gt; 才能使用 for 循环。
    猜你喜欢
    • 1970-01-01
    • 2018-04-10
    • 2019-02-22
    • 2011-11-24
    • 1970-01-01
    • 1970-01-01
    • 2016-10-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多