Joe
Joe

Reputation: 4183

Client-side Validation fails for DropDownList on int field, IE browser

Win 7 Ult, IE9. The following list is passed to a DropDownList in the view through a view bag.

List<int> test1ddl = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

It was working fine for a few days and then suddenly started failing Client-side Validation with a “The field Test1 must be a number” when posted (it never reaches the POST Action). It fails when I pass a List or List . If I replace the DropDownList with an EditorFor the POST works and the filed is updated.

The only thing I had done on the computer was to re-install IIS, this is occurring in debug using IIS Express.

When I restored a backup to the computer the problem went away. I subsequently deployed an App to the internet using this DropDownList setup and it fails with the “must be a number” error with IE 8 on XP. Chrome and FireFox work fine. It also works fine with IE9 on Win7.

I also found that I don’t even have to click on Save, just clicking out of the browser window to take it out of focus, after selecting the DropDownList, brings up the validation error. This happens with both the string and int List. This also occurs with IE8 on XP with the deployed Web App.

I restored a backup of the failing Win7 configuration and it then failed again in debug using IE9 on IIS Express. If I open up the deployed site with IE9 in this configuration it works fine. (I can’t test Chrome with IIS Express because in the failing configuration VS2010 will not use the default browser, only IE. That is a separate question if anyone has an idea on why that is happening).

At this point I don’t think there is anything wrong with the actual code but it seems to be a browser problem. Since XP and IE8 is still a very common desktop setup I need to get this to work.

My question is why do some browsers generate the Validation error?

Thanks, Joe

If you create a new MVC3 Web App and add these files you can re-create my ddlTestDB app.

Add this to the _Layout.cshtml menu.         <li>@Html.ActionLink("Test", "Index", "Test")</li>

*************** Controller     TestController.cs    **********
using System.Collections.Generic;
using System.Web.Mvc;
using ddltest.Models;
using ddltest.Infrastructure;

namespace ddltest.Controllers
{
  public class TestController : Controller
  {
    ddlTestDb _db = new ddlTestDb();

    public ActionResult Index()
    {
      var model = _db.Tests;
      return View(model);
    }

    public ActionResult Edit(int id)
    {
      var test = _db.Tests.FindById(id);
      //List<string> test1ddl = new List<string> { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" };
      List<int> test1ddl = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
      ViewBag._Test1 = test1ddl;
      return View(test);
    }

    [HttpPost]
    public ActionResult Edit(int id, FormCollection collection)
    {
      var test = _db.Tests.FindById(id);

      if (TryUpdateModel(test)) { return RedirectToAction("Index"); }

      return View(test);
    }
  }
}

********************** Class          Test.cs **************
namespace ddltest.Models
{   public class Test   {   public int Id { get; set; }     public int Test1 { get; set; }  }  }

********************** Static data  ddlTestDb.cs *****************
using System.Collections.Generic;

namespace ddltest.Models
{
  public class ddlTestDb
  {
    static ddlTestDb()
    {
        _tests = new List<Test>();
        _tests.Add(new Test { Id = 1, Test1 = 0 });
        _tests.Add(new Test { Id = 2, Test1 = 1 });
        _tests.Add(new Test { Id = 3, Test1 = 2 });
        _tests.Add(new Test { Id = 4, Test1 = 1 });
    }

    public IList<Test> Tests { get { return _tests; } }

    static List<Test> _tests;
  }
}

*********************  Extensions.cs ************
using System.Collections.Generic;
using System.Linq;
using ddltest.Models;

namespace ddltest.Infrastructure
{
  public static class TestExtensions
  {
    public static Test FindById(this IList<Test> tests, int id)
    {
      return tests.Single(t => t.Id == id);
    }
  }
}

******************  Views\Test\Edit.cshtml ****************
@model ddltest.Models.Test

@{
    ViewBag.Title = "Edit";
}

<h2>Edit</h2>

<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>

@using (Html.BeginForm()) {
    @Html.ValidationSummary(true)
    <fieldset>
        <legend>Test</legend>

        @Html.HiddenFor(model => model.Id)

        <div class="editor-label">
            @Html.LabelFor(model => model.Test1)
        </div>
        <div class="editor-field">
            @Html.DropDownList("Test1", new SelectList(ViewBag._Test1))
            @*@Html.EditorFor(model => model.Test1)*@
            @Html.ValidationMessageFor(model => model.Test1)
        </div>

        <p>
            <input type="submit" value="Save" />
        </p>
    </fieldset>
}

<div>
    @Html.ActionLink("Back to List", "Index")
</div>

***************Views\Test\ Index.cshtml ************
@model IEnumerable<ddltest.Models.Test>

@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>

<p>
    @Html.ActionLink("Create New", "Create")
</p>
<table>
    <tr>
         <th>
            Id
        </th>
        <th>
            Test1
        </th>
        <th></th>
    </tr>

@foreach (var item in Model) {
    <tr>
        <td>
            @Html.DisplayFor(modelItem => item.Id)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Test1)
        </td>
        <td>
            @Html.ActionLink("Edit", "Edit", new { id=item.Id }) |
            @Html.ActionLink("Details", "Details", new { id=item.Id }) |
            @Html.ActionLink("Delete", "Delete", new { id=item.Id })
        </td>
    </tr>
}
</table>

Upvotes: 3

Views: 2083

Answers (1)

Darin Dimitrov
Darin Dimitrov

Reputation: 1039100

Your <option> elements don't have values. Look at em:

<select id="Test1" name="Test1">
    <option>0</option>
    <option>1</option>
    <option>2</option>
    <option>3</option>
    <option>4</option>
    <option>5</option>
    <option>6</option>
    <option>7</option>
    <option>8</option>
    <option>9</option>
</select>

See the problem? None of them have a value attribute, so nothing gets posted to the server and when you try to bind nothing to an integer you get what you get: an error message.

So:

List<int> test1ddl = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
ViewBag._Test1 = test1ddl.Select(x => new SelectListItem
{
    Value = x.ToString(),
    Text = x.ToString()
});

and then:

@Html.DropDownList("Test1", (IEnumerable<SelectListItem>)ViewBag._Test1)

Look now:

<select id="Test1" name="Test1">
    <option value="0">0</option>
    <option value="1">1</option>
    <option value="2">2</option>
    <option value="3">3</option>
    <option value="4">4</option>
    <option value="5">5</option>
    <option value="6">6</option>
    <option value="7">7</option>
    <option value="8">8</option>
    <option value="9">9</option>
</select>

Much better. Oh and please don't use ViewBag. Use view models and the strongly typed versions of the helpers such as Html.DropDownListFor.

Upvotes: 10

Related Questions