0

I've got a view that outputs a table of projects and each project has a cell where it has a dropdown list of staff members. Lets call it project leader.

It prepopulates the list and selects the current project leader. The user can then select a new one via the dropdown. There is also another dropdownlist for another value, lets call it deputy

ie: Project # - DropDownList1 - DropDownList2 Project # - DropDownList1 - DropDownList2

(edit: see this pic for my view, project number, then leader then deputy. Dropdowns are set to current values but will be changed and submit button at bottom of page (not in pic) will be clicked) mvc view

There is also a default value of 'needs updating' which is set when a staff member who was a leader/deputy has left the company (their name is no longer in the list so it defaults to that).

What I want to do, is have a submit button, so the user goes through and selects new values and then hits submit. It then posts these to the server for updating.

My question is, how does this work in terms of the action method? I've named each dropdown list ProjectLeader#PROJECTID# and ProjectDeputy#PROJECTID# where #PROJECTID# is the number.

@Html.DropDownList(
                     "ProjectLeader" + item.Project.ProjectId,
                       new SelectList(item.AllStaff, "StaffId", "FullName",
                          item.Project.Leader.StaffId),
                           "NEEDS UPDATING!",
                          new { className = "Nominated" }
                     )

So when I submit that form, my post values are a along the lines of

ProjectLeader254=2&ProjectDeputy254=5&ProjectLeader255=6

etc

How do I handle this in my action method? Idealy I'd have an Dictionary of Project:StaffMember for both Leaders and Deputies ie

 [HttpPost]
    public ActionResult UpdateNominations(IDictionary<Project, StaffMember>
        projectLeaders, IDictionary<Project, StaffMember> projectDeputies)

Or possibly just dictionary of int-int for both, with the ProjectId as key and StaffId as value, as I'll probably have to look them up in the database anyway.

edit: here is my current ViewModel

    public class ProjectLeaderUpdateViewModel
{
    public Project Project{ get; set; }
    public bool LeaderCompleted { get;set; }
    public bool DeputyCompleted{ get;set;}
    public IEnumerable<StaffMember> AllStaff { get; set; } 

}

I am guessing I'll need to create a custom model binding? Can anyone give me some advice on how I should go about this?

RodH257
  • 3,552
  • 5
  • 36
  • 46

1 Answers1

2

I'd create a ViewModel in this situation to represent a single project. I'm assuming a project has a single Leader, Deputy and an OldLeader.

ViewModel:

public class Project {
    public int Leader { get; set; }
    public int Deputy { get; set; }
    public int OldLeader { get; set; }

    // Add more properties as required.
}

I'm assuming that you've got a way to get all the staff objects. The view Edit.aspx:

<% using (Html.BeginForm()) { %>
    <%= Html.DropDownListFor(p => p.Leader, new SelectList(StaffRepository.AllStaff, "StaffId", "FullName")) %>
    <%= Html.DropDownListFor(p => p.Deputy, new SelectList(StaffRepository.AllStaff, "StaffId", "FullName")) %>
    <%= Html.DropDownListFor(p => p.OldLeader, new SelectList(StaffRepository.AllStaff, "StaffId", "FullName"), "Needs updating!") %>
<% } %>

Controller:

public ActionResult Edit(int id) {
    // Get the project you're editing. Do this with whatever method you're already using - EF4, NHibernate etc.
    Project p = ProjectRepository.GetById(id);
    return View(p);
}

[HttpPost]
public ActionResult Edit(Project p) {
    // p should have it's properties bound by the default model binder.
}

The default model binder should bind the form data to the view model for you.

Updating per comments

You can model bind a list of ViewModels quite easily to:

Form partial:

~/Shared/EditorTemplates/ProjectLeaderUpdateViewModel.ascx

<%@ Control Inherits="ViewUserControl<ProjectLeaderUpdateViewModel>" %>
<%= Html.HiddenFor(p => p.Project.ProjectId) %>
<%= Html.DropDownListFor(p => p.Project.Leader.StaffId, new SelectList(p.AllStaff, "StaffId", "FullName")) %>
<%= Html.DropDownListFor(p => p.Project.Deputy.StaffId, new SelectList(p.AllStaff, "StaffId", "FullName")) %>
<%= Html.DropDownListFor(p => p.Project.OldLeader.StaffId, new SelectList(p.AllStaff, "StaffId", "FullName"), "Needs updating!") %>

~/Views/Edit.aspx

<%@ Page Inherits="ViewPage<IList<ProjectLeaderUpdateViewModel>>" %>

<% using (Html.BeginForm()) { %>
    <% for (int i = 0; i < Model.Data.Count; i++) { %>
        <%= Html.EditorFor(p => p[i]) %>
    <% } %>
<% } %>

~/Controllers/ProjectController.cs

public ActionResult Edit() {
    var projects = ProjectRepository.All();
    return View(projects);
}

[HttpPost]
public ActionResult Edit(ICollection<ProjectLeaderUpdateViewModel>) {
    // Hopefully, collection will contain all the bound view model objects.
}

Note that this is all typed up without checking, so there might be some errors.

Soliah
  • 1,376
  • 2
  • 13
  • 24
  • the main problem with this is that I have multiple dropdowns and am trying to batch them all, see my post I've added a screenshot of the view and also the code for my current ViewModel. Any ideas on how I adapt your method to work for multiple ones? – RodH257 Nov 22 '11 at 07:27
  • I've updated my answer to include some stuff for binding collections of view models. More resources here: http://stackoverflow.com/questions/5018654/how-to-model-bind-to-a-listviewmodel – Soliah Nov 22 '11 at 09:11
  • @RodH257: The default modelbinder can bind to a collection as well. See the action parameter of type `ICollection`. You should get a collection of `Project` objects when posting the form. – Jan Nov 22 '11 at 09:14