0

I've got a table with a list of event registrations for a particular customer. I have dropdowns in each row that allow changing the customer's registration status for the given event in the row. However, their current status isn't being set in the list; the first item is selected in every list.

ViewModel:

public class EditRegistrationViewModel
{
    public int SiteMemberId { get; set; }
    public string Name { get; set; }
    public List<EditParticipantViewModel> Participants { get; set; }
}

public class EditParticipantViewModel
{
    public int ParticipantId { get; set; }

    [Display(Name = "Event ID")]
    public int EventId { get; set; }

    [Display(Name = "Name")]
    public string Name { get; set; }

    [Display(Name = "Start Date")]
    public string StartDateTime { get; set; }

    [Display(Name = "Participation Status")]
    public int SelectedParticipationStatus { get; set; }
    public List<SelectListItem> ParticipationStatusList { get; set; }
}

View (just the C# for loop block)

@for (int i = 0; i < Model.Participants.Count; i++)
{
    <tr>
        @Html.HiddenFor(m => m.Participants[i].ParticipantId)
        <td class="text-right">@Model.Participants[i].EventId</td>
        <td>@Model.Participants[i].Name</td>
        <td>@Model.Participants[i].StartDateTime</td>
        <td>@Html.DropDownListFor(
            m => m.Participants[i].SelectedParticipationStatus,
            Model.Participants[i].ParticipationStatusList,
            new { @class = "form-control" })</td>
    </tr>
}

Controller (populating the initial value, but the dropdown isn't set to it)

foreach (var i in participantQuery)
{
    EditParticipantViewModel theItem = new EditParticipantViewModel()
    {
        ParticipationStatusList = tblEnum.GetSelectListForConstraintId(CRConstants.PARTICIPATION_STATUS_CONSTRAINT_ID),
        ParticipantId = i.a.aID,
        EventId = i.c.aID,
        Name = i.c.tName,
        SelectedParticipationStatus = i.b.nParticipationStatus ?? 0
    };
    DateTime? startDate = i.d.GetStartDateTime();
    theItem.StartDateTime = startDate.HasValue ? startDate.Value.ToString() : "-";
    theViewModel.Participants.Add(theItem);
}

Here's the code that gets the select list:

public static List<SelectListItem> GetSelectListForConstraintId(int constraintId)
{
    CCSContext db = new CCSContext();
    List<SelectListItem> theList = new List<SelectListItem>();
    List<tblEnum> enumList = db.Enum.Where(x=>x.nConstraintID == constraintId).OrderBy(x=>x.nOrder).ToList();
    foreach (var i in enumList)
    {
        SelectListItem theItem = new SelectListItem()
        {
            Text = i.tDisplayName,
            Value = i.nIndex.ToString()
        };
        theList.Add(theItem);
    }
    return theList;
}
Tieson T.
  • 20,774
  • 6
  • 77
  • 92
benpva16
  • 446
  • 6
  • 26
  • As long as there's value in `SelectedParticipationStatus`, this should work.Could you post `GetSelectListForConstraintId` as well? – adiga Sep 18 '17 at 19:02
  • @adiga I've added the method. Essentially I just loop through the list of statuses in the database, make a selectlistitem out of each one, and return the list. – benpva16 Sep 18 '17 at 19:06
  • @adiga Yeah, that's what's confusing me in particular, as I have other places in my code that does this just fine, just not a list where each row has a dropdown. – benpva16 Sep 18 '17 at 19:07
  • 1
    I just rememebred there is an issue with looping. Try `@Html.DropDownListFor( m => m.Participants[i].SelectedParticipationStatus, new SelectList(Model.Participants[i].ParticipationStatusList, "Value","Text", Model.Participants[i].SelectedParticipationStatus ), new { @class = "form-control" })` – adiga Sep 18 '17 at 19:19
  • @adiga Same result. I'm going to try this answer below too. – benpva16 Sep 18 '17 at 19:24
  • updated the comment. Forgot to add the `selectedValue` param – adiga Sep 18 '17 at 19:28

2 Answers2

1

When you have to use the html helper methods inside a loop to render the input elements and you would like model binding to work flawlessly, It is better to use EditorTemplates. With that you do not need to write a lot of code looping/manipulating the input element names etc.

Simply create a new folder called EditorTemplates under ~\Views\Shared\ or ~\Views\YourControllerName and create a view with the same name as of the class name you use as the type of the collection item ( EditParticipantViewModel.cshtml)

Have the below code there to render each row

@model EditParticipantViewModel
<tr>
    <td class="text-right">@Model.EventId @Html.HiddenFor(m => m.ParticipantId)</td>
    <td>@Model.Name</td>
    <td>
        @Html.DropDownListFor(
                        m => m.SelectedParticipationStatus,
                        Model.ParticipationStatusList, new { @class = "form-control" })
    </td>
</tr>

Now in the main view which is strongly typed to EditRegistrationViewModel, call the Html.EditorFor helper method

@model EditRegistrationViewModel
<h2>Form using Editor template</h2>
@using (Html.BeginForm("Index", "Home"))
{
  <table>
    @Html.EditorFor(f=>f.Participants)
  </table>
  <input type="submit" />
}

This will render the table rows for each items in Participants collection of your view model and for each row, the desired option will be pre selected (assuming you are setting the correct SelectedParticipationStatus property value for each item in the GET action and it matches with one of the option's value attribute value.)

Model binding will also work when you submit the form.

Shyju
  • 214,206
  • 104
  • 411
  • 497
0

There is an issue with binding DropDownListFor in a loop. You can either use EditorTemplates or you can try something like this:

@Html.DropDownListFor(
        m => m.Participants[i].SelectedParticipationStatus,
        new SelectList(Model.Participants[i].ParticipationStatusList, "Value","Text", 
           Model.Participants[i].SelectedParticipationStatus),
        new { @class = "form-control" })
adiga
  • 34,372
  • 9
  • 61
  • 83