Question

I am having a problem with unobtrusive validation because I have a collection of controls and all have the same name. I would appreciate any input and will consider and entirely different approach

My controls need the same name because they bind with a collection in the model and MVC4 uses control names for this (this is working). Unobtrusive validation finds the control based on the name so it’s always finding the last one.

Here are all the details:

I have a collection of Strings in my model.

public List<String> Signatures { get; set; }

The strings are bound to a collection of textBoxes in the view. The number of textBoxes is based on a selection from a radio button. If they choose 0 none are shown, choose 1 and one is shown, 2 and I show 2, up to a max of three.

I just put 3 textBoxes in the view and use client side jQuery to show or hide based on the radio button selected.

In the view, each textbox is an html Helper class bound with the model. Here is an example with two textboxes. (I add the validation span manually under each)

@Html.TextBoxFor(m => m.Signatures, new { @class = " firstSigLabel " })
<span class="field-validation-valid" data-valmsg-for="Signatures" data-valmsg-replace="true"></span>
@Html.TextBoxFor(m => m.Signatures, new { @class = " secondSigLabel})
<span class="field-validation-valid" data-valmsg-for="Signatures" data-valmsg-replace="true"></span>

When save is clicked the action method correctly binds the two textBoxes with the Signatures collection.

Now I need to add validation, which depends on the textBoxes that I am actually showing (the number from the radio button). I want the client side to work the same as it would if I just let MVC handle it (unobtrusive validation). We have a library that depends on this for some common tasks (like showing an indicator).

When I show a text box, I use jQuery to add the attributes needed for unobtrusive validation (and remove them when I hide it). That way, if it is shown it is required and if it is hidden it is not required. Here are the inputs once they are visible:

<input id="Signatures" class=" firstSigLabel" type="text" value="" name="Signatures"  data-val-required="Enter a first Signature Label." data-val="true"></input>
<input id="Signatures" class=" secondSigLabel" type="text" value="" name="Signatures" data-val-required="Enter a second Signature Label." data-val="true"></input>

I also re-register the from with unobtrusive validation:

$(theForm).removeData('validator');
$.validator.unobtrusive.parse($(theForm));

My problem is that unobtrusive validation uses the 'data-valmsg-for' attribute in the SPAN, to find the element by name. However, I have two inputs with that name and it’s always finding the last one.

I have tried changing the ID (Signatures1, Signatures2) but that does nothing.

I cannot figure out how to change the name in the html helper, but if I do it will break the model binding in the controller.

I know this is not simple – it is really obtrusive validation. I don’t know how to accomplish this with annotations since it is dependent on client side actions.

I assume if I used ValidationMessageFor on three textboxes, bound to the same model property, I would still get this problem.

Is there a way to overcome this?

Am I missing something?

Can you think of a better approach?

Was it helpful?

Solution

Instead of binding to the collection, bind to an element in the collection. This will work even when the collection is empty. Here is an example:

@Html.TextBoxFor (m => m.Signatures[0], new { @class = "firstSigLabel" })
@Html.TextBoxFor (m => m.Signatures[0], new { @class = "firstSigLabel" })

When this is rendered, it will look like this (I included only the relevant markup):

<input id="Signatures_0_" name="Signatures [0]"></input>
<input id="Signatures_1_" name="Signatures [1]"></input>

Now each control has a different name and unobtrusive validation should work. You can leave your collection a List datatype because that implements ICollection.

OTHER TIPS

If you define the collection of Signatures as an

ICollection<String> Signatures

then when you reference the fields from the View you should be able to reference them as Signatures[0], Signatures[1], etc. and the model binder will automatically use the

ICollection<T>.Add

method to ensure they are all properly mapped back to the collection.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top