Search code examples
asp.net-mvc-4html-helper

How HtmlHelper know data is on the ViewBag?


I was watching a tutorial about HtmlHelper for DropDown https://www.youtube.com/watch?v=79aYSOcmpV8

Around min 8, he is reading the db to replace some hardcode values.

To pass list of Departments from the controller, store them in "ViewBag"

public ActionResult Index()
{
    // Connect to the database
    SampleDBContext db = new SampleDBContext();
    // Retrieve departments, and build SelectList
    ViewBag.Departments = new SelectList(db.Departments, "Id", "Name");

    return View();
}

Last step.

Now in the "Index" view, access Departments list from "ViewBag"

@Html.DropDownList("Departments", "Select Department") 

I dont see anything like strong type model on the view.


So how the Helper know Departments refers to a value in the ViewBag?


Solution

  • When you add a value to ViewBag, it is also added to the ViewData property of ViewContext when the view is generated. The DropDownList() overload that your using in equivalent to passing a null SelectList in

    @Html.DropDownList("Departments", null, "Select Department")
    

    in which case, internally, the helper searches the ViewData property to find a matching key which is an IEnumerable<SelectListItem> (which "Departments" is). You can see the relevant code in the private static MvcHtmlString SelectInternal() method of the source code

    // If we got a null selectList, try to use ViewData to get the list of items.
    if (selectList == null)
    {
        selectList = htmlHelper.GetSelectData(name);
        ....
    }
    

    Note that the example in the tutorial is a poor approach, using 'magic' strings and requiring you to access the value in the POST method using Request.Form["Departments"]. A far better approach is to use a view model and strongly bind to your view model, for example

    public class MyViewModel
    {
        public int SelectedDepartment { get; set; }
        public IEnumerable<SelectListItem> DepartmentList { get; set; }
        ...
    }
    

    and the GET method would be

    public ActionResult Create()
    {
        MyViewModel model = new MyViewModel
        {
            DepartmentList = new SelectList(db.Departments, "Id", "Name");
        };
        return View(model);
    }
    

    and in the view

    @model MyViewModel
    ....
    @Html.DropDownListFor(m => m.SelectedDepartment, Model.DepartmentList, "Select Department")
    

    and post the form back to

    [HttpPost]
    public ActionResult Create(MyViewModel model)