0

I have a razor mvc 5 view where I want to validate some values.

The Model

public partial class PersonViewModel
{
   [Required]
   public string FirstName { get; set; }

   [Required]
   public string LastName { get; set; }
}

The View

@using (Html.BeginForm("Process", "SomeController", FormMethod.Post, new { id = "processForm", enctype = "multipart/form-data" }))
{
    @Html.AntiForgeryToken()

<!-- Buttons -->
<div class="row btn-row">
    <div class="col-md-12">
        <div class="pull-right">
            <button class="btn btn-success"><span class="glyphicon glyphicon-floppy-save"></span> Save</button>
        </div>
    </div>
</div>
<!-- some more html -->
    for (int i = 0; i < Model.Count; i++)
    {
        <tr>
            <td>
                @Html.CheckBoxFor(m => m[i].ToBeProcessed, new {@id = "[" + i + "[.ToBeProcessed"})
            </td>
            <td>
                @Html.TextBoxFor(m => m[i].FirstName, new {@id = "[" + i + "].FirstName"})
                @Html.ValidationMessageFor(m => m[i].FirstName, "Please enter a value", new {@class = "text-danger"})
            </td>
            <td>
                @Html.TextBoxFor(m => m[i].LastName, new {@id = "[" + i + "].LastName"})
                @Html.ValidationMessageFor(m => m[i].LastName, "Please enter a value", new {@class = "text-danger"})
            </td>
        </tr>
    }
    <!-- some more html -->
    } <!-- form close -->

I only want to validate the rows where the checkbox [i].ToBeProcessed is checked. The text-boxes in the other rows should be ignored.

In my jQuery I have the following code:

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

    <script type="text/javascript">
        $(function(){
            $("#processForm").validate({
                rules: {"[0].FirstName": {required: "[0].ToBeProcessed:checked"},
                       {"[1].FirstName": {required: "[1].ToBeProcessed:checked"},
                       {"[0].LastName": {required: "[0].ToBeProcessed:checked"},
                       {"[1].LastName": {required: "[1].ToBeProcessed:checked"} // note that this is only fixed for testing purposes
                });
            });
        </script>
}

Now, when running the code, check the first checkbox and hit the submit button, all textfields are validated instead of only the ones in the first row.

5
  • how does your model look? Commented Sep 7, 2015 at 17:58
  • @Dandy I've added the model. Commented Sep 7, 2015 at 18:21
  • 1
    Check the Conditional Validation using DataAnnotation, specifically RequiredIfAttribute. It also has a client side validation. Commented Sep 7, 2015 at 19:53
  • Use a foolproof [RequiredIfTrue("ToBeProcessed")] or similar attribute applied to your FirstName and LastName properties - you get both client and server side validation Commented Sep 7, 2015 at 23:07
  • @StephenMuecke foolproof is the solution that is working for me. I've provided an answer for anyone facing the same question Commented Sep 8, 2015 at 6:58

2 Answers 2

1

You can add the validation as follows, not the best way but works

 @for (int i = 0; i < Model.Count; i++)
            {
                <tr>
                    <td>
                        @Html.CheckBoxFor(m=>m[i].ToBeProcessed, new { @id = "ToBeProcessed_" + i })
                    </td>
                    <td>
                        @Html.TextBoxFor(m=>m[i].FirstName, new { @id ="FirstName_"+i})
                        @Html.ValidationMessageFor(m => m[i].FirstName)
                    </td>
                    <td>
                        @Html.TextBoxFor(m => m[i].LastName, new { @id = "LastName_"+i})
                        @Html.ValidationMessageFor(m => m[i].LastName)
                    </td>
                </tr>
            }

and generate your validation rules as

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

    <script type="text/javascript">
        $(function(){
        $('#processForm').validate();

        @for(int i = 0; i < Model.Count; i++)
        {
        <text>
        $("#FirstName_@i").rules("add", {
            required: "#ToBeProcessed_@i:checked"
        });

        $("#LastName_@i").rules("add", {
            required: "#ToBeProcessed_@i:checked"
        });

        </text>
        }
    });
    </script>
}

For more control, you should use @Zabavsky article link.

Sign up to request clarification or add additional context in comments.

5 Comments

It will not work - there are no controls that have the id attribute in the format #ToBeProcessed_@i
it will be rendered as ToBeProcessed_0, ToBeProcessed_1 etc. Its inside razor for loop.
It will not. It will be id="_0__ToBeProcessed" name="[0].ToBeProcessed" Not to mention its pointless since the real validation takes place on he server and this does not address that (ModelState will still be invalid)
Thats strange. I get it exactly rendered as <input checked="checked" data-val="true" data-val-required="The ToBeProcessed field is required." id="ToBeProcessed_0" name="[0].ToBeProcessed" type="checkbox" value="true">. I have html attribute object in @Html.CheckboxFor . And the server validation does not care about elements ids.
No it cares about the name and value of the properties and the attributes applied to them so this does nothing to address server side validation which is the critical validation (client side validation is a nice bonus but can easily be bypassed). And OP has already added an answer showing the correct way to do this
0

Solved it by using Foolproof Because I'm using MVC 5, not all the javascript files should be included. The steps to make it work:

Step 1 Download Foolproof via nugget (Install-Package foolproof)

Step 2 In your BundleConfig.cs add:

bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
                    "~/Scripts/jquery-{version}.js"));

bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
                    "~/Scripts/jquery.validate*"));

bundles.Add(new ScriptBundle("~/bundles/mvcfoolproof").Include(                       
                    "~/Scripts/MvcFoolproofJQueryValidation.min.js",
                    "~/Scripts/mvcfoolproof.unobtrusive.min.js"));

Step 3 Add the bundles to the view (make sure JQuery is loaded first. For me, I've loaded the JQuery bundle in my _Layout.cshtml) and the following bundles in my specific view:

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

Step 4 In my model I have

public partial class Person
{
    public bool ToBeProcessed {get; set;}

    [RequiredIfTrue("ToBeProcessed")]
    public string FirstName {get; set;}

    [RequiredIfTrue("ToBeProcessed")]
    public string LastName {get; set;}
}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.