Reputation: 4319
I'm new to MVC, so apologies for the noob question!
I have the following model (TimeEntry), used for entering timesheet details:
namespace MVCLogin.Models
{
public class TimeEntry
{
public int TimeEntryID { get; set; }
public int TaskTypeID { get; set; }
[ForeignKey("TaskTypeID")]
public virtual TaskType TaskType { get; set; }
public double MonHours { get; set; }
public double TueHours { get; set; }
public double WedHours { get; set; }
public double ThuHours { get; set; }
public double FriHours { get; set; }
}
}
Task type is based on the following model:
namespace MVCLogin.Models
{
public class TaskType
{
public int TaskTypeID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
}
I use the following partial view which is used for entering the timesheet details:
<div class="col-md-1">
@Html.EditorFor(model => model.TaskType)
@Html.ValidationMessageFor(model => model.TaskType)
</div>
<div class="col-sm-1">
@Html.TextBoxFor(model => model.MonHours , new { style = "width:100%", @class = "hours mon" })
@Html.ValidationMessageFor(model => model.MonHours)
</div>
<div class="col-sm-1">
@Html.TextBoxFor(model => model.TueHours, new { style = "width:100%", @class = "hours tue" })
@Html.ValidationMessageFor(model => model.TueHours)
</div>
<div class="col-sm-1">
@Html.TextBoxFor(model => model.WedHours, new { style = "width:100%", @class = "hours wed" })
@Html.ValidationMessageFor(model => model.WedHours)
</div>
<div class="col-sm-1">
@Html.TextBoxFor(model => model.ThuHours, new { style = "width:100%", @class = "hours thu" })
@Html.ValidationMessageFor(model => model.ThuHours)
</div>
<div class="col-sm-1">
@Html.TextBoxFor(model => model.FriHours, new { style = "width:100%", @class = "hours fri" })
@Html.ValidationMessageFor(model => model.FriHours)
</div>
I want the task type field to be a drop down, but I can't figure out how to wire it up. The classes and data are right, as if I scaffold a controller (using Visual Studio's Add Controller tool) for the TimeEntry class it works fine:
<div class="form-group">
@Html.LabelFor(model => model.TaskTypeID, "TaskTypeID", htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.DropDownList("TaskTypeID", null, htmlAttributes: new { @class = "form-control" })
@Html.ValidationMessageFor(model => model.TaskTypeID, "", new { @class = "text-danger" })
</div>
</div>
How can I get the dropdownlist to work in the partial view?
Upvotes: 0
Views: 73
Reputation: 4440
Per your request: So the ViewBag is a property that enables you to dynamically share values from your controller to the view. Since it is dynamic, keep in mind that you will need to explicitly cast objects so the razor engine knows what they are at runtime. Note that upon reading about it (further reading at bottom), ViewBag is short lived and only lives within the current request so in the event that you have a model state error in your controller, you will need to reload the data into your viewbag.
This will help keep your models/view models very clean.
So on to the code, Test setup: I have my main page (Index)and I have a partial view (AddSomething) that is loaded in my main Index page. Given your models, here is what my controller looks like. Please Note that I am loading up the viewbag in my parent page initially but if an error occurs on my submit then I need to reload my viewbag since the dynamic object is short lived.
DALHelper dalHelp = new DALHelper();
public ActionResult Index()
{
//Get a list of task types from the data access layer
//Cast them into a SelectItemList and pass them into viewBag
ViewBag.TaskDD = dalHelp.GetTaskTypes().Select(a => new SelectListItem { Text = a.Name, Value = a.TaskTypeID.ToString() }).ToList();
return View();
}
public ActionResult AddSomething()
{
TimeEntry te = new TimeEntry();
return View(te);
}
[HttpPost]
public ActionResult AddSomething(TimeEntry te)
{
if (ModelState.IsValid)
{
//call my Data Access Layer to add new entry and redirect to wherver on success
if (dalHelp.CreateTimeEntry(te))
{
return RedirectToAction("Index");
}
else
{
//something happened during adding entry into DB, write whatever code to handle it gracefully
//I need to reload my viewbag since it has since been cleared (short lived)
ViewBag.TaskDD = dalHelp.GetTaskTypes().Select(a => new SelectListItem { Text = a.Name, Value = a.TaskTypeID.ToString() }).ToList();
ModelState.AddModelError("TimeEntryID", "An Error was encountered...");
return View(te);
}
}
else
{
//I need to reload my viewbag since it has since been cleared (short lived)
ViewBag.TaskDD = dalHelp.GetTaskTypes().Select(a => new SelectListItem { Text = a.Name, Value = a.TaskTypeID.ToString() }).ToList();
return View(te);
}
}
In my partial View I need to cast my viewbag item since it is a dynamic type so the razor engine can process: Also not that I am using Dropdownlistfor unstead of just dropdownlist:
@model deletemeweb2.Models.TimeEntry
@using (Html.BeginForm("AddSomething", "Home"))
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>TimeEntry</h4>
@Html.ValidationMessageFor(model => model.TimeEntryID, "", new { @class = "text-danger" })
<hr />
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
<div class="form-group">
@Html.LabelFor(model => model.TaskTypeID, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.DropDownListFor(model => model.TaskTypeID, (IEnumerable<SelectListItem>)ViewBag.TaskDD, new { @class = "form-control" })
@Html.ValidationMessageFor(model => model.TaskTypeID, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.MonHours, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.MonHours, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.MonHours, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.TueHours, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.TueHours, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.TueHours, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.WedHours, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.WedHours, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.WedHours, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.ThuHours, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.ThuHours, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.ThuHours, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.FriHours, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.FriHours, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.FriHours, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
Further reading about ViewBag and like properties when dealing with different controller needs: http://www.c-sharpcorner.com/blogs/viewdata-vs-viewbag-vs-tempdata-in-mvc1
Upvotes: 1
Reputation: 146
The null parameter in your Html.DropDownList
is the data of the Dropdown and it's a Ienumerable<SelectList>
.
In order to pass the data. You need to transform your TaskType
List into SelectListItem
List (You can do this in the controller or somewhere else just before sending Model from Controller to View) and put it in your Model like this
namespace MVCLogin.Models
{
public class TimeEntry
{
public int TimeEntryID { get; set; }
public int TaskTypeID { get; set; }
[ForeignKey("TaskTypeID")]
public virtual TaskType TaskType { get; set; }
public IEnumerable<SelectListItem> TaskTypeSelectListItems { get; set; }
public double MonHours { get; set; }
public double TueHours { get; set; }
public double WedHours { get; set; }
public double ThuHours { get; set; }
public double FriHours { get; set; }
}
}
And the dropdown code will changed to
<div class="form-group">
@Html.LabelFor(model => model.TaskTypeID, "TaskTypeID", htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.DropDownList("TaskTypeID", model => model.TaskTypeSelectListItems, htmlAttributes: new { @class = "form-control" })
@Html.ValidationMessageFor(model => model.TaskTypeID, "", new { @class = "text-danger" })
</div>
</div>
Notice that the selected value will bind to "TaskTypeID" on your model when you post/get to the server.
Upvotes: 2