Monday 17 October 2011

HTML.DropDownList Partial View selectedItem WebGrid

I have a partial view which renders a list of child entities for the main view/entity.

EG A Student View, bound to a StudentModel. Each student has a bunch of courses. Each course has a Score attribute.

So the Student page should have a Grid of Courses. In each row of the grid, which represent a course, there should be a drop down list allowing the user to change the Score for the course, for that Student (don't let the Students get to this page!).

Getting this working in MVC proved a little challenging.

I used a Partial View within the StudentView. The Partial View was bound to an ICollection<COURSE> object - this is done via the call

@model ICollection<MYUNI_BUSINESS_DOMAIN.COURSE>


Now the Model for the Partial View is a collection of Courses.

So we want to render a table, with each row having a drop down list bound to a collection of SCORE objects (eg HighDistinction, Distinction, Credit, Pass, Fail) and make sure that when the page loads, each COURSE has the correct SCORE pre-selected, if any.

There are a few tricks to this:
  1.  Define the collection of objects that you want to be displayed in the drop down list within the Controller of the main entity, eg the StudentController, and put it in the ViewBag so that the partial view can access it:

    ViewBag.SCORE_TYPES = GetScoreTypes(); // this returns IEnumerable<SCORE_TYPE>
  2. Define a "SelectList" object within the Partial View, passing into it the collection of SCORE_TYPES you defined in the Controller. Make sure you declare the forth parameter, this is what tells .NET which field of your Model is to be used to determine the 'SelectedValue' upon initial page load:

    new SelectList(ViewBag.SCORE_TYPES, "SCORE_TYPE_ID", "SCORE_TYPE_DESC", item.SCORE_TYPE_ID);
    
  3. Then assign "SelectList" to the HTML.DropDownList helper. 
You can do all this with a WebGrid object or manually marking up the table yourself.
Using a WebGrid (this is the code for the Partial View)

@model ICollection<MYUNI_BUSINESS_DOMAIN.COURSE>

@{
    var grid = new WebGrid(Model, canPage: false, canSort: true, defaultSort: "", selectionFieldName: "editCourse");
}
@grid.GetHtml(
tableStyle: "FullWidth",
columns:
grid.Columns(
    grid.Column(format: (item) => Html.DropDownList("nameOfDDL",new SelectList(ViewBag.SCORE_TYPES, "SCORE_TYPE_ID", "SCORE_TYPE_DESC", item.SCORE_TYPE_ID), "Pick a score...")),
    grid.Column("STUDENT_COURSE_DESC", header: "Course Description")    
    ))

Using a Manual Markup (this is the code for the Partial View)

@model ICollection<MYUNI_BUSINESS_DOMAIN.COURSE>
 
<table class="FullWidth">
    <tr>
        <th>Score Type</th>
        <th>Course Description</th>
        <th></th>
    </tr>
@{               
    foreach (var item in Model)
    {
        var selectList = new SelectList(ViewBag.SCORE_TYPES, "SCORE_TYPE_ID", "SCORE_TYPE_DESC", item.SCORE_TYPE_ID);
        <tr>
            <td>
            @Html.DropDownList("nameOfDDL", selectList, "Pick a score...")
            </td>
            <td>@item.STUDENT_COURSE_DESC</td>
        </tr>        
    }      
}
</table>

1 comment:

  1. There is a flaw to my logic above!!!

    It all works fine for displaying data, but if you want to post the data back to an action method which is expected a fully instantiated Model object (eg COURSE) with the correct SCORE_TYPE_ID selected, you need to make sure you give the Html.DropDownFor helper a 'Name' parameter which matches the name of the property you want it the selected item to map to.

    For example, the above DropDownList should be re-written as such:

    @Html.DropDownList("SCORE_TYPE_ID", selectList, "Pick a score...")

    For more information see my post:

    "MVC 3 Html.DropDownList - How to Post Data back to an Action Method "

    ReplyDelete