1

我正在开发一个项目请求网站,并充实了一个项目,其中从事项目的员工可以添加对完成项目区域所需时间的估计。我希望此页面将任意数量的估计发布回编辑操作,但我在这里遗漏了一些东西。

在我看来,我正在使用Html.BeginCollectionItemfrom here helper 来协助处理Html.RenderPartial("_WorkEstimateEditorRow", item);.

编辑:我现在正在为 AJAX 部分苦苦挣扎——我看到值回来了Request.Form,并且属性与我的WorkEstimate类正确匹配,但是即使我将控制器操作更改为只接受IEnumerable<WorkEstimate>估计值,它也是 null .

这是一行的输出值,使用Html.BeginCollectionItem

<input data-val="true" data-val-number="The field EstimateId must be a number." data-val-required="The EstimateId field is required." id="estimates_d32afd89-987e-4d09-a847-abfc33dde220__EstimateId" name="estimates[d32afd89-987e-4d09-a847-abfc33dde220].EstimateId" value="0" type="hidden">
<input id="estimates_d32afd89-987e-4d09-a847-abfc33dde220__Estimator" name="estimates[d32afd89-987e-4d09-a847-abfc33dde220].Estimator" value="" type="hidden">
<span class="editor-label">
    <label for="estimates_d32afd89-987e-4d09-a847-abfc33dde220__WorkArea">Work Area</label>
</span>
<span class="editor-field">
    <input class="text-box single-line" id="estimates_d32afd89-987e-4d09-a847-abfc33dde220__WorkArea" name="estimates[d32afd89-987e-4d09-a847-abfc33dde220].WorkArea" value="" type="text">
    <span class="field-validation-valid" data-valmsg-for="estimates[d32afd89-987e-4d09-a847-abfc33dde220].WorkArea" data-valmsg-replace="true"></span>
</span>

<span class="editor-label">
    <label for="estimates_d32afd89-987e-4d09-a847-abfc33dde220__Hours">Hours</label>
</span>
<span class="editor-field">
    <input class="text-box single-line" data-val="true" data-val-number="The field Hours must be a number." data-val-required="The Hours field is required." id="estimates_d32afd89-987e-4d09-a847-abfc33dde220__Hours" name="estimates[d32afd89-987e-4d09-a847-abfc33dde220].Hours" value="0" type="number">
    <span class="field-validation-valid" data-valmsg-for="estimates[d32afd89-987e-4d09-a847-abfc33dde220].Hours" data-valmsg-replace="true"></span>
</span>
<a href="#" class="deleteRow">[x]</a>
<hr>

name属性是在Request.Form发布操作中遇到的。我还尝试更改我的 Controller 操作,以便它收到一个IEnumerable<WorkEstimate>没有任何更改的。

模型

public class EstimationManager
{
    public EstimationManager()
    {
        CurrentUser = new WebUser();
        Project = null;

        EstimationData = new WorkEstimateRepository();
        Estimates = new List<WorkEstimate>();
    }

    public EstimationManager(ApprovedProject project, WebUser currentUser)
        : this(project, currentUser, new WorkEstimateRepository())
    { }

    public EstimationManager(ApprovedProject project, WebUser currentUser, IWorkEstimateRepository repository)
    {
        Project = project;
        CurrentUser = currentUser;

        EstimationData = repository;

        Estimates = EstimationData.Get(Project);
    }

    IWorkEstimateRepository EstimationData { get; set; }

    public WebUser CurrentUser { get; set; }
    public ApprovedProject Project { get; set; }
    public List<WorkEstimate> Estimates { get; set; }

    public bool CurrentUserHasWorkerAccess
    {
        get
        {
            return CurrentUser != null
                && CurrentUser.AccessLevels.HasWorkerAccess
                && (Project == null || CurrentUser.AccessLevels.WorkerUnit == Project.CurrentWorkerUnit);
        }
    }
}

控制器动作

public class EstimatesController : BaseSessionController
{
    private IProjectRepository _projects;
    private IWorkEstimateRepository _estimates;
    EstimationManager manager;

    public EstimatesController()
        : this(new WorkEstimateRepository(), new ProjectRepository())
    { }

    public EstimatesController(IWorkEstimateRepository estimates, IProjectRepository projects)
    {
        _estimates = estimates;
        _projects = projects;
    }

    //
    // GET: /Estimates/Edit/5

    public ActionResult Edit(int id)
    {
        ApprovedProject details = _projects.Get(id);
        manager = new EstimationManager(details, CurrentUser, _estimates);

        return View(manager);
    }

    //
    // POST: /Estimates/Edit/5

    [HttpPost]
    public ActionResult Edit(int id, FormCollection collection)
    {
        ApprovedProject details = _projects.Get(id);
        manager = new EstimationManager(details, CurrentUser, _estimates);

        if (TryUpdateModel(manager)
            && _estimates.TrySave(manager))
        {
            return RedirectToAction("Details", new { id = id });
        }
        else
        {
            foreach (WorkEstimate item in manager.Estimates)
            {
                foreach (RuleViolation currentViolation in item.GetRuleViolations())
                {
                    ModelState.AddModelError(item.WorkArea + currentViolation.PropertyName, currentViolation.ErrorMessage);
                }
            }

            return View(manager);
        }
    }
}

看法

@model ProjectRequests.Web.Models.Project.Estimates.EstimationManager

@{
    ViewBag.Title = "Edit Estimate Details for " + Model.Project.Name;
}

<h2>Edit Estimate Details for @Model.Project.Name</h2>

@using (Html.BeginForm()) {
    @Html.AntiForgeryToken()
    @Html.ValidationSummary(true)

    <fieldset>
        <legend>Estimation Manager</legend>
        <span id="editorRows">
            @foreach (ProjectRequests.Web.Models.Project.Estimates.WorkEstimate item in Model.Estimates)
            {
                if (Model.CurrentUser == item.Estimator)
                {
                    Html.RenderPartial("_WorkEstimateEditorRow", item);
                }
                else
                {
                    Html.DisplayFor(modelItem => item);
                }
            }
        </span>

        @if (Model.CurrentUserHasWorkerAccess)
        {
            @Html.ActionLink("Add another estimate.", "BlankEstimateRow", null, new { id = "addItem" })

            <p>
                <input type="submit" value="Save" />
            </p>
        }
    </fieldset>
}

<div>
    @Html.ActionLink("Back to List", "Index")
</div>

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")

    <script type="text/javascript">
        $().ready(function () {
            $("#addItem").click(function () {
                $.ajax({
                    url: this.href,
                    cache: false,
                    success: function (html) {
                        $("#editorRows").append(html);
                    }
                });
                return false;
            });

            $("#editorRows").on("click", ".deleteRow", function () {
                $(this).closest(".editorRow").remove();
                return false;
            });
        });
    </script>

}

当我的 Edit 操作的签名为public ActionResult Edit(int id, FormCollection collection)时,视图会返回适当的数据,但TryUpdateModel实际上不会更新Estimates属性。相反,public ActionResult Edit(int id, EstimationManager newManager)也不设置Estimates。手动筛选FormCollection以从页面中提取值的想法散发出一股大代码气味,这让我认为我应该以不同的方式处理这个问题。

4

2 回答 2

0

看起来您正在遍历Estimates模型的集合,并WorkEstimate从 each 中获取一个属性的值Estimate,然后用它渲染局部视图WorkEstimate或用它制作标签。

我猜由于视图上没有Estimate带有索引的特定项目,模型绑定器无法确定它们需要绑定到Estimates集合中,在您使用的示例中public ActionResult Edit(int id, EstimationManager newManager)

此外,显然具有索引值对于集合项很重要;for在视图中使用而不是和索引器更好地使用foreach集合项,如下所示:

@for (int i = 0, i < Model.Estimates.Count; i++)
{
    Html.EditorFor(m => m.Estimates[i].WorkEstimate);
}

作为一个粗略的例子。

于 2013-06-07T16:31:38.483 回答
0

我有一个类似的问题。为了让 MVC 能够很好地处理集合,我发现最好在视图中使用for循环而不是foreach循环。使用Html帮助器时,使用表达式中的索引器访问值,这将导致 Razor 以表单控件的名称使用该索引器。在您的控制器上,接受一个集合(肯定是数组,您可能可以逃脱IEnumerable<>),它应该可以工作。

由于您使用类作为模型而不是直接集合,因此您可能需要使用BindAttributeon 您的操作参数让 MVC 知道请求数据将具有前缀,或者使用具有相同属性的另一个类名称作为您的EstimationManager类中的属性作为操作参数。

一旦您开始通过索引器访问,只需查看 MVC 输出的源代码,我认为表单名称将帮助您了解正在发生的事情。

于 2013-06-07T16:21:17.930 回答