MVC 4中可变长度列表和验证错误的问题

时间:2014-09-16 23:08:40

标签: c# asp.net-mvc asp.net-mvc-4 model-binding

我目前正在使用asp.net-mvc 4处理一个表单,该表单提交一个存储在IList中的可变数量的子对象的对象。

我遇到了一个问题,如果返回错误,验证消息会附加到IList中的错误对象。如果列表中的第一个对象被删除而第二个对象(然后成为第一个对象)中有错误,则错误的对象在页面重新加载时无法获得正确的验证结果。返回错误时,索引为3的列表中的对象的索引值为2,这也会同时更改先前(可能正确的)选择。

有没有办法更改列表中哪些对象以前的验证错误附加到?

编辑:验证消息位于正确的位置,因为“ValidationIndex”正在运行,突出显示是我遇到问题的地方。

我尝试在列表中的对象中添加“ValidationIndex”,以将验证错误发送到正确的位置。这适用于错误消息,但不正确的对象仍然突出显示并更改了字段。

删除删除除最后一个对象之外的任何内容的能力显然是解决此问题的一种方法,但我真的希望找到一种方法来保留这些功能。我还想避免在每次添加新框时都必须发出http请求。

这是一张显示问题的imgur专辑: http://imgur.com/a/borWG

以下是我正在使用的代码示例:

容器类:

public class TestProduct : Product
{
    public IList<TestItem> TItems { get; set; }

    public TestProduct()
    {
        TItems = new List<TestItem>();
    }
}

列表中的类:

public class TestItem : IValidatableObject
{
    [Display(Name = "Select 1")]
    public string select1 { get; set; }

    [Display(Name = "Select 2")]
    public string select2 { get; set; }

    // Added to try and Identify where the validation errors need to go
    public int ValidationIndex { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (select1 == null)
        {
            yield return new ValidationResult("Required", new string[] { "select1" });
        }

        if (select2 == null)
        {
            yield return new ValidationResult("Required", new string[] { "select2" });
        }

        if (select1 == select2)
        {
            yield return new ValidationResult("The options cannot be the same", new string[] { "select2", "select1" });
        }
    }
}

观点:

@model TestProduct

@{
    List<SelectListItem> selectOptions = new List<SelectListItem>()
    {
        new SelectListItem { Text = "Choose...", Value = "" },
        new SelectListItem { Text = "Option 1", Value = "o1" },
        new SelectListItem { Text = "Option 2", Value = "o2" },
        new SelectListItem { Text = "Option 3", Value = "o3" },
        new SelectListItem { Text = "Option 4", Value = "o4" }
    };
}

<script type="text/javascript">
    $(document).ready(function () {
        var itemContainer = $('#items');
        var i = $('#items .item-option').length;

        $('#addItem').on("click", function (e) {
            if ($('#items .item-option').length <= 15) {
                var newField = '<fieldset class="item-option"> ' +
                        '<legend>Item ' + (i + 1) + '</legend>' +
                        '<input type="hidden" name="TItems.Index" value="' + i + '" /> ' +
                        '<input type="hidden" name="TItems[' + i + '].ValidationIndex" value="' + i + '" /> ' +
                        '<div class="line"> ' +
                            '<div> ' +
                                '<div class="label"><label for="select1-' + i + '">@Html.DisplayNameFor(model => model.TItems[0].select1)</label></div> ' +
                                '<div> ' +
                                    '<select id="select1-' + i + '" name="TItems[' + i + '].select1"> ' +
                                        @foreach (SelectListItem so in selectOptions)
                                        {
                                            <text>
                                                '<option value="@so.Value">@so.Text</option> ' +
                                            </text>
                                        }
                                    '</select> ' +
                                '</div> ' +
                            '</div> ' +
                            '<div> ' +
                                '<div class="label"><label for="select2-' + i + '">@Html.DisplayNameFor(model => model.TItems[0].select2)</label></div> ' +
                                '<div>' +
                                    '<select id="select2-' + i + '" name="TItems[' + i + '].select2"> ' +
                                        @foreach (SelectListItem so2 in selectOptions)
                                        {
                                            <text>
                                                '<option value="@so2.Value">@so2.Text</option> ' +
                                            </text>
                                        }
                                    '</select> ' +
                                '</div> ' +
                            '</div> ' +
                        '</div> ' +    
                        '<div class="pos-rb"><button class="remItem smalltext">Remove</button></div> ' +
                    '</fieldset>';
                itemContainer.append(newField);
                $('.remItem:hidden').show();
                i++;
            }
            e.preventDefault();
        });

        $('#items').on("click", '.remItem', function (e) {
            var n = $('#items .item-option').length;
            if (n >= 2) {
                $(this).closest('.item-option').remove();
            }
            if (n == 2) {
                $('.remItem').hide();
            }
            e.preventDefault();
        });
    });
</script>

<h3>Test Product</h3>
<div class="indented">
    <div id="items" class="items question">
        @if (Model.TItems.Count > 0)
        {
            for (int i = 0; i < Model.TItems.Count; i++)
            {
                int validationIndex = Model.TItems[i].ValidationIndex;
                <text>
                    <fieldset class="item-option">
                        <legend>Item @(i + 1)</legend>
                        <input type="hidden" name="TItems.Index" value="@i" />
                        <input type="hidden" name="TItems[@i].ValidationIndex" value="@i" />
                        <div class="line">
                            <div>
                                <div class="label">@Html.LabelFor(model => model.TItems[i].select1)</div>
                                <div>
                                    @Html.DropDownListFor(model => model.TItems[i].select1, new SelectList(selectOptions, "Value", "Text", Model.TItems[i].select1))<br />
                                    @Html.ValidationMessageFor(model => model.TItems[validationIndex].select1)
                                </div>
                            </div>
                            <div>
                                <div class="label">@Html.LabelFor(model => model.TItems[i].select2)</div>
                                <div>
                                    @Html.DropDownListFor(model => model.TItems[i].select2, new SelectList(selectOptions, "Value", "Text", Model.TItems[i].select2))<br />
                                    @Html.ValidationMessageFor(model => model.TItems[validationIndex].select2)
                                </div>
                            </div>
                        </div>

                        <div class="pos-rb">
                            <button class="remItem smalltext" @if (Model.TItems.Count == 1) { <text> style="display: none;" </text> }>Remove</button>
                        </div>
                    </fieldset>
                </text>
            }
        }
        else
        {
            <text>
                <fieldset class="item-option">
                    <legend>Item 1</legend>
                    <input type="hidden" name="TItems.Index" value="0" />
                    <input type="hidden" name="TItems[0].ValidationIndex" value="0" />
                    <div class="line">
                        <div>
                            <div class="label">@Html.LabelFor(model => model.TItems[0].select1)</div>
                            <div>
                                @Html.DropDownListFor(model => model.TItems[0].select1, new SelectList(selectOptions, "Value", "Text"))
                            </div>
                        </div>
                        <div>
                            <div class="label">@Html.LabelFor(model => model.TItems[0].select2)</div>
                            <div>
                                @Html.DropDownListFor(model => model.TItems[0].select2, new SelectList(selectOptions, "Value", "Text"))
                            </div>
                        </div>
                    </div>
                    <div class="pos-rb"><button class="remItem smalltext" style="display: none;">Remove</button></div>
                </fieldset>
            </text>
        }
    </div>
    <button id="addItem" class="smalltext" style="margin-left: 2em;">Add Another Item</button>
    <br />
    <div style="margin: 1em;"><input type="submit" value="Calculate" /></div>
</div>

修改: 我想出了一个使用JavaScript的相当混乱的工作。每次删除一个项目时,它都会遍历并替换表单中所有输入及其标签的所有名称,ID和字段。它不是很优雅,但它到目前为止工作......

这是新的删除项事件处理程序:

$('#items').on("click", ".remItem", function (e) {
    var n = $("#items .item-option").length;
    if (n >= 2) {
        $(this).closest(".item-option").remove();
    }
    if (n == 2) {
        $(".remItem").hide();
    }
    $("#items").find(".item-option").each(function (index) {
        $(this).find(".item-index").val(index);
        $(this).find("select, input, textarea").each(function () {
            var name1 = $(this).attr('name');
            if (name1 != null) {
                var name2 = name1.replace(/TItems\[\d+\]/, "TItems[" + index + "]");
                $(this).attr("name", name2);
            }
            var id1 = $(this).attr("id");
            if (id1 != null) {
                var id2 = id1.replace(/_\d+__/, "_" + index + "__");
                $(this).attr("id", id2);
            }
        });
        $(this).find("label").each(function () {
            var for1 = $(this).attr("for");
            if (for1 != null) {
                var for2 = for1.replace(/_\d+__/, "_" + index + "__");
                $(this).attr("for", for2);
            }
        });
        $(this).find("legend").html("Item " + (index+1));
    });
    i--;
    e.preventDefault();
});

0 个答案:

没有答案