简体   繁体   中英

Passing IEnumerable<SelectListItem> to Controller

I am building a IEnumerable in my HTTPGet and passing it to my view. My issue is getting the selected info back to the controller using a hiddenfor.

    [HttpGet]
    public ActionResult Index(int id)
    {
        RANKDModel model = new RANKDModel();
        model.thingList = new List<thingsModel>();
        List<SelectListItem> thingRank = new List<SelectListItem>();

        var tblThingGroup = db.tblThingGroup.Where(x => x.thingCollectionID == id).Include(t => t.tblThing).Include(t => t.tblThingCollection).OrderBy(x => x.displayOrder);

        model.thingRank = new SelectList(thingRank, "Value", "Text");

        int i = 1;

        foreach (var thing in tblThingGroup)
        {
            thingRank.Add(new SelectListItem()
            {
                Value = thing.tblThing.thingID.ToString(),
                Text = i.ToString()
            });
            model.thingRank = new SelectList(thingRank, "Value", "Text");

            thingsModel things = new thingsModel();

            things.thingID = thing.tblThing.thingID;
            things.thingName = thing.tblThing.thingName;
            things.thingImgPointer = thing.tblThing.thingImgPointer;

            model.thingList.Add(things);
            i++;
        }

        return View(model);
    }

    [HttpPost]
    public ActionResult Index(RANKDModel model, IEnumerable<SelectListItem> thingRank)
    {
        //check model.Selecteditem
        return View(model);
    }

My view displays the drop downs but I cannot seem to get the values passed back to the controller on Post. Below is my view. The commented lines are failed attempts. I would rather not use ajax here. Since my value and my text fields are all I need, I feel like I should be able to pass them back to the controller even if they are not part of the model (which would be nice but I wasn't successful there either.)

@using (Html.BeginForm())
{
    <div class="col-md-8">
        @Html.TextBoxFor(modelItem => Model.firstName, new { @class = "form-control" })
        @Html.TextBoxFor(modelItem => Model.lastName, new { @class = "form-control" })
    </div>

    <table class="table">
        @Html.HiddenFor(modelItem => Model.thingRank)

        @for (int i = 0; i < Model.thingList.Count; i++)
        {
            string imagePointer = "/Areas/RANKD/thingImages/" + Model.thingList[i].thingImgPointer;
            <tr>
                <td>
                    @Html.HiddenFor(modelItem => Model.thingList[i].thingID)

                    @Html.DropDownListFor(modelItem => modelItem.thingRank, (IEnumerable<SelectListItem>)ViewBag.thingRank, "Rank it Bro.") @*Model.thingRank.Items as IEnumerable<SelectListItem>)*@

                    @*@Html.HiddenFor(modelItem => modelItem.thingRank[Model.thingRank(i)].Value)*@

                    @Html.HiddenFor(modelItem => modelItem.thingRank[Model.thingRank.IndexOf(i)].Value)

                    @*@Html.HiddenFor(modelItem => modelItem.thingRank)*@

                    @Html.DisplayFor(modelItem => Model.thingList[i].thingName)
                </td>
                <td>
                    <img src="@imagePointer" alt="@Model.thingList[i].thingName" height="75" />
                </td>
            </tr>
        }

    </table>

    <input type="submit" value="RANK IT" class="btn btn-default" />
}

Am I just missing something in my hiddenfor?

Adding the code in my RANDModel

[Serializable]
public class RANKDModel
{
    public string firstName { get; set; }
    public string lastName { get; set; }
    public List<thingsModel> thingList { get; set; }
    public IEnumerable<SelectListItem> thingRank { get; set; }
}

You are creating many dropdowns within the for loop. I guess you need them so the user can select something in each row. Right now you have this:

@Html.DropDownListFor(modelItem => modelItem.thingRank, 
    (IEnumerable<SelectListItem>)ViewBag.thingRank, "Rank it Bro.")

So the Razor view engine will create many HTML tags and set the name attribute to thingRank . You do not want that. You want to give each element a unique name . Also when something is selected in each dropdownlist, you will need it later during form submission in your POST method. Therefore, add one more property to your model:

public List<int> SelectedRanks { get; set; }

In the GET action, you will need to set that property to the same number of items as thingList because you need a dropdown for each one of them. Thus do this:

// ...other code
model.SelectedRanks = new List<int>(model.thingList.Count);
return View(model);

Now in your view, use this code in your for loop (please note the indexing operation here):

@Html.DropDownListFor(modelItem => this.Model.SelectedRanks[i], 
    this.Model.thingRank, "Rank it Bro.")

Now each dropdownlist will have a unique name . For example, if you have 3 dropdowns, their names would be: SelectedRanks[0] , SelectedRanks[1] , and SelectedRanks[2] .

When the user submits the form, you can retrieve the selected values from each dropdownlist like this:

[HttpPost]
public ActionResult Index(RANKDModel model)
{
    // model.SelectedRanks will have the value selected
    // For example, model.SelectedRanks[0] will have the value selected
    // in the first dropdown, model.SelectedRanks[1] will have value 
    // selected in the second dropdown and so on.
}

Some Final Suggestions

  1. Please give your properties meaningful names. For example, thingList is not a meaningful name.
  2. Use Pascal Notation for class names and property names. So instead of thingList , it should be ThingList .
  3. If you have an acronym which is 2 chars, use upper case for it, otherwise use Pascal Notation. For example, RANKDModel should be RankdModel (shouldn't it be spelled RankedModel ?)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM