20

Closed

DropDownListFor() Unobtrusive Validation Problem

description

I have a project using MVC 3 RC and the Razor View engine.

I have several DropDownListFor() elements which are required. I set the [Required(..)] atribute for my model, but no unobtrusive validation is being emitted for the<select> elements. All other unobtrusive validation is being emitted as needed.

I can provide sample code if requested.

This is a critical issue for this project. Is it possible to address this issue before RTM?

Thanks,
counsellorben

P.S.
I also have some custom HtmlHelpers which create markup (essentially a UserControl) where the user's input value is placed by client-side script into a hidden input. Unobtrusive validation is not being emitted for my hidden input element. This is also a very important issue for my project.

In the interim, I have written a hack into a custom HtmlHelper [DropDownListUovFor()] to read the FieldValidationMetadata ModelClientValidationRules, and add them directly as HTML5 attributes, which are then sent to the underlying DropDownListFor() helper. Voila, I have my unobtrusive validation for my DropDownList. Ugly (ok, ok VERY ugly), but functional. Also, the same ugly hack serves as a work-around for my other custom Htmlhelpers.

file attachments

Closed Nov 18, 2011 at 9:58 PM by
Fxied in MVC4

comments

sweetCoffee wrote Dec 6, 2010 at 1:41 PM

I'm having exactly the same problem and that's the reason why I have to stay at MVC 2... too bad!

msimmons wrote Dec 7, 2010 at 9:02 PM

Seems like this should be higher than a low impact. If we are to use unobtrusive validation this is critical.

msimmons wrote Dec 7, 2010 at 9:16 PM

I made this work for me by adding @class="required" to the attributes

sweetCoffee wrote Dec 8, 2010 at 7:23 AM

thx msimmons, it's working!
let's hope they fix this bug in the RTM version, but for now I can continue
one weird problem I have is that my dropdownlist always goes a little bit to the left when clicking an item and then clicking on to a blank side of the page - I only have this problem using Razor......

counsellorben wrote Dec 8, 2010 at 4:38 PM

After further exploration in the MVC 3 Beta source, I see that the problem is in System.Web.Mvc.Html.SelectExtensions. Near the bottom of the file, it calls GetUnobtrusiveValidationAttributes(), passing only the name value. It does not attempt to get the metadata for the select field.

In System.Web.Mvc.HtmlHelper, GetUnobtrusiveValidationAttributes(string name) then passes a null metadata value to the overload GetUnobtrusiveValidationAttributes(string name, ModelMetadata metadata). Neither attempts to find the metadata for this field.

The problem is that SelectInternal in System.Web.Mvc.Html.SelectExtensions does not accept the expression collected in the overloads for DropDownListFor() and ListBoxFor(), so it cannot derive the metadata from this expression.

This is an egregious oversight, which can be fixed in less than five minutes.

counsellorben wrote Dec 10, 2010 at 11:28 PM

This is still unfixed in RC2. Seriously?

counsellorben wrote Dec 16, 2010 at 1:05 AM

To add to my mystification, I created a new MVC 3 RC2 web application using VB Razor for an internal app for a client, and the unobtrusive validation appeared when using DropDownListFor().

So I re-created (from scratch as a new MVC 3 RC2 web application using C# Razor) my C# project where the unobtrusive validation does not appear. Still no unobtrusive validation for DropDownListFor() in that project.

Uhhh ... huh?

counsellorben wrote Dec 16, 2010 at 11:48 AM

I am mystified no more.

The unobtrusive validation is working for DropDownListFor() where it is a single instance.

In my project, I have a DropDownListFor() contained inside a table (similar to the WebForms GridControl), where each instance of the control is differentiated using ViewData.TemplateInfo.HtmlFieldPrefix. The unobtrusive validation is being emitted for input elements named in the style "prefix.fieldname" and, in MVC 2, JSON validation was emitted for input and select elements using this style.

However, in RC 2, select elements do not have unobtrusive validation emitted if the value of ViewData.TemplateInfo.HtmlFieldPrefix is set.

Please address this issue.

Thanks,
counsellorben

Haacked wrote Dec 16, 2010 at 5:41 PM

Could you attach the simplest repro possible?

counsellorben wrote Dec 16, 2010 at 7:12 PM

Phil,

Per your request, here is a tripped down example showing the problem I am experiencing. Folder is called "DropDownListFor.zip."

counsellorben

eilonlipton wrote Dec 28, 2010 at 8:06 PM

This indeed appears to be a bug in ASP.NET MVC 3. However, due to where we are in the product cycle we will look at fixing this for the next version of ASP.NET MVC.

jbeckton wrote Jan 25, 2011 at 11:22 PM

I'm having the same issues, I have the latest release version of MVC 3. I was afraid of bugs like this when I went for MVC 3 and now that I have invested many hours with MVC 3 I am stuck with half assed client validation for my forms. This is very disappointing to say the least. seems like an easy fix and one would expect to see it fixed in the release version.

mreyeros wrote Feb 3, 2011 at 4:16 PM

Adding @class="required" does seem to cause the validation to happen but it does not seem to pull the actual validation error messages that you specify, it simply returns "This field is required."

zote wrote Mar 2, 2011 at 11:18 AM

I have same problem. Sample is available here https://bitbucket.org/zote/validacaomvc under Select action.

mkchandler wrote Apr 13, 2011 at 8:20 PM

I would like to thrown in another vote for getting this fixed. It is a very annoying bug! It's a shame that a bug this big could make it into a final release, even though it was brought up well before the release. Please make sure this is fixed in MVC 4!

leniel wrote May 2, 2011 at 12:48 AM

Hi,

I was having a similar problem.

I just thought about changing this line of code inside my Create ActionResult on StudentController class:

ViewBag.StudentId = new SelectList(db.Students, "StudentId", "FullName");

to

ViewBag.Students = new SelectList(db.Students, "StudentId", "FullName");

As you can see the name of the ViewBag property was the problem. If I kept StudentId, jQuery client unobtrusive validation would not fire, that is, the DropDownList selected value would be validated only on the server side. After changing the name of the ViewBag property to Students, jQuery unobtrusive client validation started working. I'm so happy now. :D

Conclusion: naming was the problem!

Hope this helps,

Leniel Macaferi
http://www.leniel.net

leniel wrote May 2, 2011 at 12:50 AM

Oh, take a look at this issue: http://aspnet.codeplex.com/workitem/4932

PreethaA wrote May 17, 2011 at 11:46 PM

I got this working by creating a property on the Viewmodel which binds to the selecteditem in the DropDownList. an example will make it clear:

The code in the View:
           @Html.DropDownListFor(model => model.SelectedPlantID,
                                 new SelectList(Model.Plants, "Value", "Text"),
                                 " ", new { id = "ddlPlant" })

          @Html.ValidationMessageFor(model => model.SelectedPlantID)


The code in the ViewModel will be(strongly typed to the view):
            private List _plants = new List();
    [Required]
    [Display(Name = "Plant")]
    public List Plants
    {
        get
        {
            return (_plants);
        }

        set
        {
            _plants = value;
        }
    }

    public Guid SelectedPlantID
    {
        get;
        set;
    }
Note : the SelectedPlantID does not have to be a field on your model.

Hope this works for you!

pbarranis wrote Aug 15, 2011 at 10:17 PM

IMHO, it looks like the top two most-voted open issues for MVC3 issue are tightly related:
(this one) http://aspnet.codeplex.com/workitem/7629
(related) http://aspnet.codeplex.com/workitem/4932

Just in case it's not covered in the above comments clearly, the reason the above issue is confusing so many people is because if you set your SelectList to the ViewData or ViewBag using the same name as the field, it will break client-side validation. In other words, you have to NOT do this:

// In the controller:
ViewBag.ItemID = Database.Items.Select(i => new SelectListItem(){Value = i.ID, Text = i.Name});
// In the view:
@Html.DropDownList("ItemID") Instead DO this
// In the controller:
ViewBag.ItemIDList = Database.Items.Select(i => new SelectListItem(){Value = i.ID, Text = i.Name});
// In the view:
@Html.DropDownListFor(m => m.ItemID, (IEnumerable)ViewBag.ItemIDList)

If I may, I'd like to humbly suggest a fix for both issues:

Rather than providing the very simple model now called SelectList, create a new one called SelectListModel or DropDownModel defined as such:
{
// The default text to appear first as the first option in the dropdown.
// Remarks: NULL will not be added to the dropdown, but String.Empty will be.
String DefaultItemText {get;set;}

// The options to be shown in the dropdown list.
IEnumerable Options {get;set;}

// The currently-selected value.
// Remarks: If this value is not null, and there is a model passed into the view, then this value will override the value in the view's model.  However, the value in the model will be used if this value is null.
String SelectedValue;
}

Also, drop the Boolean Selected property from the SelectListItem class. DropDowns don't have multi-select, so this is hammering a square peg into a round hole.

Finally, allow the above model to be passed in the "expected" way to the ViewBag or ViewData from the controller:
ViewBag.MyFieldID = new SelectListModel() {...};
And then in the view EITHER of the following should work fine:

@Html.DropDownList("MyFieldID") @Html.DropDownList(model => model.MyFieldID)

I hope this idea will at least get considered. Obviously a number of users are confused by how DropDowns currently work :(

counsellorben wrote Sep 19, 2011 at 7:06 PM

I am pleased to confirm that this issue is resolved in MVC4 Developer Preview.

Thanks,
counsellorben

drewmiller wrote Sep 19, 2011 at 7:43 PM

Glad to hear it counsellorben. To be clear, we have not yet addressed all of the issues here on CodePlex yet (i.e., many of the bugs reported here are still bugs in MVC 4 DP), but we'll be considering them as we continue to develop MVC 4. I'll be updating them all soon, and will continue to do so throughout MVC 4's development.

AlanMosley wrote Oct 23, 2011 at 4:32 PM

This works
@Html.DropDownListFor(Function(model) model.StateId, Nothing, "State")
This doesnt
@Html.DropDownList("StateId", Nothing, "State")

Why not use the first syntax in scafolding

moflaher wrote Nov 4, 2011 at 11:54 PM

It's MVC 3, November 4, 2011 and this bug still has not been fixed.

First - most people are using this dropdownlist with a model => model.ID as their solution to this problem. The rest of us are using many dropdownlist(s) to make choices that are NOT a part of a model. So you can't use model.ID or anything using the model. Thus, we have to use the Viewbag with the dropdown list and other methods.
Also - you cannot even have this dropdownlist on the same page that was created from the scaffolding. Adding in the dropdownlist causes failure any time any field fails validation.

Does anyone know if this is going to be fixed for sure in MVC4 or if Microsoft is going to post a valid workaround?

drewmiller wrote Nov 7, 2011 at 8:35 PM

moflaher, counsellorben confirmed that this is already fixed in the Developer Preview of MVC 4. Please let us know if you find it's not working for you.