@Html.DropDownListFor not posting back to controll

2019-07-09 00:13发布

I am using @Html.DropDownListFor for the first time. Code is below.

Model:

class Student
{
    [Required]
    [Display(Name = "Name")]
    public string Name { get; set; }

    [Required]
    [Display(Name = "Roll Number")]
    public string RollNumber { get; set; }

    [Required]
    [Display(Name = "ClassId")]
    public int ClassId { get; set; }

}

class Class
{
    [Required]
    [Display(Name = "ClassId")]
    public string RollNumber { get; set; }

    [Required]
    [Display(Name = "ClassName")]
    public string RollNumber { get; set; }
}

Controller:

public ActionResult Create()
{
    Student student = new BusinessEntities.Student();
    List<Class> classes = GetAllClasses();
    ViewBag.ClassId = new SelectList(classes, "ClassId", "ClassName");
    return View(student);
}

[HttpPost]
public ActionResult Create(BusinessEntities.Student student)
{
   if (ModelState.IsValid)
   {
      //Integer has 0 by default. But in our case if it contains 0, 
      //means no class selected by user
      if(student.ClassId==0)
      {
        ModelState.AddModelError("ClassId", "Select Class to Enroll in");
        return View(student);
      }
    }
}

Student Create View:

<form  method="post">
    Select Class : 
    @Html.DropDownListFor(Model=>Model.ClassId,ViewBag.ClassId as IEnumerable<SelectListItem>, "ClassId","ClassName")
    @Html.ValidationMessageFor(Model => Model.ClassId)
    <input type="submit" value="Submit" />
</form>

Error Message:

The ViewData item that has the key 'ClassId' is of type 'System.Collections.Generic.List`1[[BusinessEntities.Class, BusinessEntities, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]' but must be of type 'IEnumerable'.

I want ClassId of Student be binded and populated automatically when posted back to Controller. Please help me to get rid of it. Thanks.

1条回答
女痞
2楼-- · 2019-07-09 00:46

You need to give the SelectList a different name that the property your binding to (say)

ViewBag.ClassList = new SelectList(classes, "ClassId", "ClassName");` 

and then

@Html.DropDownListFor(m => m.ClassId, ViewBag.ClassList as IEnumerable<SelectListItem>)

and then ensure if you return the view (for example if ModelState is invalid), that you repopulate the SelectList (as you have done in the GET method). Currently when you return the view, it is null resulting in an error, because if the second parameter is null the fallback is that the helper expects the first parameter to be IEnumerable<SelectListItem> (but its not - its typeof int)

Side notes: Do not use Model => Model.XXX (capital M) and your current use of DropDownistFor() as 2 parameters which make no sense. "ClassId" will add a label option <option value="">ClassId</option> and the last one ,"ClassName" will not do anything.

Edit

In addition, your

if(student.ClassId==0)
{
    ModelState.AddModelError("ClassId", "Select Class to Enroll in");
    return View(student);
}

is a bit pointless. student.ClassId will never be 0 unless one of the items in your GetAllClasses() has ClassId = 0. You should be using

[Required(ErrorMessage = "Select Class to Enroll in")] // add error message here
public int ClassId { get; set; }

and in the view

@Html.DropDownListFor(m => m.ClassId, ViewBag.ClassList as IEnumerable<SelectListItem>, "--please select--")

which will create the first option with a value of null. If this option were selected, then the DefaultModelBinder will attempt to set the value of ClassId = null which fails (because typeof int cannot be null) and a ModelState error is added and ModelState becomes invalid.

The in the POST method

[HttpPost]
public ActionResult Create(BusinessEntities.Student student)
{
  if (!ModelSTate.IsValid)
  {
    ViewBag.ClassList = // repopulate select list
    return View(student);
  }
  // Save and redirect
}
查看更多
登录 后发表回答