2

I have a view for entering delivery quantities on every items in the list, and sends a list of objects back to controller. I would like to validate all textboxes on the page, and I have added annotations in the model.

The problem is, I can only validate the first row (also in the output html only the first row has validation markups). Since there are generated validations on the first row, so I don't think it's about the model. Is there a way to have generated validations in all rows? If not, what are the workarounds?

    @{int i = 0;}
    @foreach (var item in Model)
    {
        <tr>
            <td>
                @Html.HiddenFor(modelItem => item.eo, new { Name = "[" + i + "].eo" })
                @Html.DisplayFor(modelItem => item.barcode)
                @Html.Hidden("[" + i + "].barcode", Model[i].barcode)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.itemno)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.cdesc)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.acost)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.qty)
            </td>
            <td>
                @Html.EditorFor(modelItem => item.dqty, new { htmlAttributes = new { Name = "[" + i + "].dqty", id = "[" + i + "].dqty", @class = "form-control" } })
                @Html.ValidationMessage("[" + i + "].dqty", "", new { @class = "text-danger" })
            </td>
        </tr>
        i++;
    }

This is the generated html for the textbox in the first row.

<input Name="[0].dqty" class="form-control text-box single-line" data-val="true" data-val-number="The field 出貨數量 must be a number." data-val-required="必須填上出貨數量" id="[0].dqty" name="item.dqty" type="text" value="10" />
<span class="field-validation-valid text-danger" data-valmsg-for="[0].dqty" data-valmsg-replace="true"></span>

And the second row onwards...

<input Name="[1].dqty" class="form-control text-box single-line" id="[1].dqty" name="item.dqty" type="text" value="7" />
<span class="field-validation-valid text-danger" data-valmsg-for="[1].dqty" data-valmsg-replace="true"></span>

The Model

[MetadataType(typeof(EorderDetailsMetaData))]
public partial class EorderDetails
{
    public string eo { get; set; }
    public string barcode { get; set; }
    public string itemno { get; set; }
    public string cdesc { get; set; }
    public Nullable<decimal> qty { get; set; }
    public Nullable<decimal> dqty { get; set; }
    public Nullable<decimal> acost { get; set; }
    public string sdate { get; set; }
    public string edate { get; set; }
    public string shop { get; set; }
    public string sname { get; set; }
    public string saddr { get; set; }
    public string shoptel { get; set; }
    public string shopfax { get; set; }
}

public class EorderDetailsMetaData
{
    [Display(Name = "訂單編號")]
    public string eo { get; set; }
    [Display(Name = "電腦條碼")]
    public string barcode { get; set; }
    [Display(Name = "貨品編號")]
    public string itemno { get; set; }
    [Display(Name = "貨品名稱")]
    public string cdesc { get; set; }
    [Display(Name = "訂購數量")]
    [DisplayFormat(DataFormatString = "{0:n0}", ApplyFormatInEditMode = true)]
    public Nullable<decimal> qty { get; set; }
    [Display(Name = "出貨數量")]
    [DisplayFormat(DataFormatString = "{0:n0}", ApplyFormatInEditMode = true)]
    [Required(ErrorMessage = "必須填上出貨數量")]
    public Nullable<decimal> dqty { get; set; }
    [Display(Name = "成本價")]
    [DisplayFormat(DataFormatString = "{0:0.##}", ApplyFormatInEditMode = true)]
    public Nullable<decimal> acost { get; set; }
    public string sdate { get; set; }
    public string edate { get; set; }
    public string shop { get; set; }
    public string sname { get; set; }
    public string saddr { get; set; }
    public string shoptel { get; set; }
    public string shopfax { get; set; }

}

2 Answers 2

2

You should be generating the collection in a for loop and let the helpers generate the correct html for you. If you inspect the html you have posted in the second snippet you will see the issue (two name attributes!)

@model IList<EorderDetails>
@using(Html.BeginForm())
{
  for(int i = 0; i < Model.Count; i++)
  {
    @Html.HiddenFor(m => m[i].eo)
    @Html.DisplayFor(m => m[i].barcode)
    ....
    @Html.EditorFor(m => m[i].dqty, new { htmlAttributes = new { @class = "form-control" } })
    @Html.ValidationMessageFor(m => m[i].dqty, new { @class = "text-danger" })
  }
  <input type="submit" />
}

Alternatively you can create a custom EditorTemplate for your model

/Views/Shared/EditorTemplates/EorderDetails.cshtml

@model EorderDetails
@Html.HiddenFor(m => m.eo)
@Html.DisplayFor(m => m.barcode)
....

and in the main view

@model IList<EorderDetails>
@using(Html.BeginForm())
{
  @Html.EditorFor(m => m)
  <input type="submit" />
}
Sign up to request clarification or add additional context in comments.

1 Comment

It works~! I know foreach and i variable is not the best practice, but I never thought it is the reason. Thank you very much Stephen.
0

To omit this strange behavior, there should:

  1. Define the property as Array, instead of ICollection or IList.
  2. Should use for in cshtml, instead of forEach.

Have no idea why this will cause the difference. But I think it should not. And I think this is a bug should be fixed.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.