Dropdown list mvc5 from other model

2019-08-19 10:37发布

问题:

First time using MVC to develop an app and have run into an issues populating a dropdown in the student create view with a list of teachers.

I have 2 models

public class Student
{

    [Required]
    [Display(Name = "ID")]
    public int ID { get; set; }
    [Required]
    [StringLength(100)]
    [DataType(DataType.Text)]
    [Display(Name = "First Name")]
    public string FirstName { get; set; }
    [Required]
    [StringLength(100)]
    [DataType(DataType.Text)]
    [Display(Name = "Last Name")]
    public string LastName { get; set; }
    [Required]
    [DataType(DataType.Date)]
    [Display(Name = "Date of Birth")]
    public DateTime? DateOfBirth { get; set; }
    [Required]
    [DataType(DataType.EmailAddress)]
    [Display(Name = "Email Address")]
    public string EmailAddress { get; set; }
    [Required]
    [DataType(DataType.PhoneNumber)]
    [Display(Name = "Contact Number")]
    public string ContactNumber { get; set; }
    [Required]
    [StringLength(100)]
    [DataType(DataType.Text)]
    [Display(Name = "Parent/Guardian Name")]
    public string ParentGuardianName { get; set; }

    public Teacher Teacher { get; set; }


    public MembershipOption MembershipOption { get; set; }

    public ICollection<Progress> Progress { get; set; }
    public ICollection<Report> Report { get; set; }
    [DataType(DataType.Date)]
    [Display(Name = "Signed Up Date")]
    public DateTime? SignedUpDate { get; set; }
    [Display(Name = "Active")]
    public bool Active { get; set; }



}

 public class Teacher
{
    [Required]
    [Display(Name = "ID")]
    public int ID { get; set; }
    [Required]
    [StringLength(100)]
    [DataType(DataType.Text)]
    [Display(Name = "First Name")]
    public string FirstName { get; set; }
    [StringLength(100)]
    [DataType(DataType.Text)]
    [Display(Name = "Last Name")]
    public string LastName { get; set; }
    [Display(Name = "Active")]
    public bool Active { get; set; }
    [Required]
    [EmailAddress]
    [Display(Name = "Email")]
    public string Email { get; set; }

}

}

I have the create view of the student model that looks like this

    <div class="form-group">
    @Html.LabelFor(model => model.ParentGuardianName, htmlAttributes: new { @class = "control-label col-md-2" })
    <div class="col-md-10">
        @Html.DropDownListFor(m => m.Teacher, // 1. Store selected value in Model.State;
                                         // when page is rendered after postback,
                                         // take selected value from Model.State.

                                       // 2. Take list of values from Model.States
                                       Model.Teacher,

                                       // 3. Text for the first 'default' option
                                       "- Please select a Teacher -",

                                       //4. A class name to assign to <select> tag
                                       new { @class = "form-control" })
        @Html.ValidationMessageFor(model => model.Teacher, "", new { @class = "text-danger" })
    </div>
</div>

The dropdownlist for Teachers throws an exception due to Teacher being null.

How do i go about adding the teachers drop down list to this student create cshtml?

Im assuming i need to get the teachers when the student model is called, but not sure where it needs to be put in the studentscontroller?

回答1:

The second parameter should be a list of SelectListItem's so that the helper method can use that list to generate the SELECT options. But you are trying to pass the Teacher navigational property instead of that!

You should create a view model for your view, which has properties to accept the form input values. Since you want to add a dropdown to the view, add 2 properties to the view model. one for the selected value and one for the list of items we need to use to render the dropdown

public class CreateStudent
{
    [Required]
    [StringLength(100)]
    [Display(Name = "First Name")]
    public string FirstName { get; set; }

    [Required]
    [StringLength(100)]
    [Display(Name = "Last Name")]
    public string LastName { get; set; } 

    // to do : Add other properties AS NEEDED BY THE VIEW
    // Do not just copy all the properties from Entity class

    public List<SelectListItem> Teachers { set; get; }

    [Required]
    [DisplayName("Select Teacher")]
    public int SelectedTeacherId { set; get; }
}

Now in your GET action, you need to create an object of this view model, populate the Teachers collection property and send the object to the view

public ActionResult Create()
{
    var vm = new CreateStudent();
    vm.Teachers = GetTeachers();
    return View(vm);
}

private List<SelectListItem> GetTeachers()
{
    return db.Teachers.Select(a => new SelectListItem
    {
        Value = a.Id.ToString(),
        Text = a.Name
    }).ToList();
}

Now in your view, which is strongly typed to our view model, we can use DropDownListFor helper method to render the SELECT element

@model CreateStudent
@using(Html.BeginForm())
{
    @Html.LabelFor(a=>a.FirstName)
    @Html.TextBoxFor(a=>a.FirstName)

    @Html.LabelFor(a=>a.LastName)
    @Html.TextBoxFor(a=>a.LastName)

    @Html.LabelFor(a=>a.SelectedTeacherId)
    @Html.DropDownListFor(a=>a.SelectedTeacherId , Model.Teachers, "select one")

    <input type="submit" />
}

Now in your HttpPost action, use the same view model as your action method parameter, and read the values from it's properties and use that to create a new object of your entity class and save it

[HttpPost]
public ActionResult Create(CreateStudent model)
{
  if(ModelState.IsValid)
  { 
     var s = new Student { FirstName=model.FirstName };
     s.LastName = model.LastName;
     // to do : Map other property values as well from view model
     db.Students.Add(s);
     db.SaveChanges();
     return RedirectToAction("Index");
  }
  //Make sure to RELOAD the Teachers list for dropdown
  model.Teachers = GetTeachers();
  return View(model);
}