0

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>

enter image description here

How can I get the dropdownlist to work in the partial view?

2 Answers 2

2

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.

Sign up to request clarification or add additional context in comments.

3 Comments

You shouldn't litter you models/view models with things like select list items when you only care about the selected ID from the list of values. Load your select list items into your view bag and cast appropriately in the view itself from the view bag.
Thanks @TravisActon. Can you expand on this approach? Very new to MVC so not too clear on how view bags work.
His ideal is move the data source of the dropdown (TaskTypeSelectListItems in my example) out side of the Model/ViewModel so that the model wont get dirty. In this case is moving the TaskTypeSelectListItems in to the Viewbag and access it directly in the View
1

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.