Diceble
Diceble

Reputation: 807

Asp.net MVC multiple select for List property

I'm fairly new to ASP.Net MVC so forgive me for anything that should just be obvious.

I have an object that contains a property that is a list. I only don't know how I should implement this in the create. this is the object:

public class TeamMember
{
    public int TeamMemberId { get; set; }
    public string FristName { get; set; }
    public string LastName { get; set; }
    public DateTime BirthDate { get; set; }
    public string Biographie { get; set; }
    public virtual Image Image { get; set; }
    public virtual List<DanGrade> DanGrades { get; set; }
}

In the create view I want to be able to select multiple Dangrades.

I tried to modify an editor Template for it that looks like this:

@using BudoschoolTonNeuhaus.Models
@model BudoschoolTonNeuhaus.Models.TeamMember

@{
    var db = new ApplicationDbContext();
    var danGrades = db.DanGrades.ToList();
}

<select multiple name="@ViewData.TemplateInfo.HtmlFieldPrefix" class="dropdown">
    @foreach (var dan in danGrades)
    {
        <option value="@">
            @dan.DanGradeId: @dan.BudoSport, @dan.Grade
        </option>
    }
</select>

but this does not give the result that I thought it would, its just showing mutiple dangrade labels in the create view that you can see here:

@model BudoschoolTonNeuhaus.Models.TeamMember

@{
    ViewBag.Title = "Create";
    Layout = "~/Views/Shared/_Admin_Layout.cshtml";
}
<div class="wrapper">
    <h2>Create</h2>

    @using (Html.BeginForm())
    {
        @Html.AntiForgeryToken()

        <div class="form-horizontal">
            <h4>TeamMember</h4>
            <hr />
            @Html.ValidationSummary(true, "", new { @class = "text-danger" })
            <div class="form-group">
                @Html.LabelFor(model => model.FristName, htmlAttributes: new { @class = "control-label col-md-2" })
                <div class="col-md-10">
                    @Html.EditorFor(model => model.FristName, new { htmlAttributes = new { @class = "form-control" } })
                    @Html.ValidationMessageFor(model => model.FristName, "", new { @class = "text-danger" })
                </div>
            </div>

            .... // controls for other properties of model

            <div class="form-group">
                @Html.LabelFor(model => model.DanGrades, htmlAttributes: new { @class = "control-label col-md-2" })
                <div class="col-md-10">
                    @Html.EditorFor(model => model.DanGrades, new { htmlAttributes = new { @class = "form-control" } })
                    @Html.ValidationMessageFor(model => model.DanGrades, "", new { @class = "text-danger" })
                </div>
            </div>

            <div class="form-group">
                @Html.LabelFor(model => model.Image, htmlAttributes: new { @class = "control-label col-md-2" })
                <div class="col-md-10">
                    <input type="file" id="Image" name="Image" hidden />
                </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>
    }

    <div>
        @Html.ActionLink("Back to List", "Index")
    </div>
</div>

current HTML output:

Current output (not what i want)

Thanks for you help in advance!

Upvotes: 2

Views: 4264

Answers (3)

user3559349
user3559349

Reputation:

To create a <select multiple> you use the ListBoxFor() method in your view.

But your model needs two properties to generate a listbox, a IEnumerable<int> to bind the selected values to (assumes the ID proeprty of DanGrade is typeof int), and an IEnumerable<SelectListItem> to display the <option> elements.

You editing data, so always start with a view model

public class TeamMemberVM
{
    public int? TeamMemberId { get; set; }
    ....
    [Display(Name = "DanGrades")]
    public IEnumerable<int> SelectedDanGrades { get; set; }
    public IEnumerable<SelectListItem> DanGradesList { get; set; }
}

and your view will be

@model yourAssembly.TeamMemberVM
....
@Html.ListBoxFor(m => m.SelectedDanGrades, Model.DanGradesList, new { @class="dropdown" })

and your controller methods will be

public ActionResult Create()
{
    TeamMemberVM model = new TeamMemberVM();
    ConfigureViewModel(model);
    // For an Edit method, your would set the existing selected items here
    model.SelectedDanGrades = ...
    return View(model);
}
public ActionResult Create(TeamMemberVM model)
{
    if (!ModelState.IsValid)
    {
        ConfigureViewModel(model); // repopulate the SelectList
        return View(model);
    }
    // model.SelectedDanGrades contains the ID's of the selected options
    // Initialize an instance of your data model, set its properties based on the view model
    // Save and redirect
}
private void ConfigureViewModel(TeamMemberVM model)
{
    IEnumerable<DanGrade> danGrades = db.DanGrades();
    model.DanGradesList = danGrades.Select(x => new SelectListItem
    {
        Value = x.DanGradeId.ToString(),
        Text = x.??? // the name of the property you want to use for the display text
    });
}

Note also that your view has a file input so your view model needs a HttpPostedFileBase property to bind the file to

public HttpPostedFileBase Image { get; set; }

and in the view

@Html.TextBoxFor(m => m.Image, { new type ="file" })

Upvotes: 1

Tyler S. Loeper
Tyler S. Loeper

Reputation: 836

So you are trying to save a list of custom objects inside your object. First of all, know that if you try to save teammember to a database your list of objects will not save. I've experienced this same issue and its needs some special configuring to get just that to work.

Second you can't select custom objects from a < select >. Select returns string[] to your controller. So objects, no. You can't return complex items like that using select directly.

What you can do is return a string[] and use the individual strings (maybe it contains name, maybe it contains id?) and then use that array to pull each object to your teammember object in the controller from the dangrade db context (I'm assuming that is where they are stored).

So for example if you Go back to your controller and add (string[] dangrades) to your parameters. Your parameters now looks something like this (string[] dangrades, Bind[blahblah] ... teammember).

Now after referencing the other database you can do as follows

   teammember.Dangrades = new list<Dangrade>();
   foreach(string item in dangrades)
   {
   var dangradeselected = from x in db.dangrades where x.name = item select x;      
   var dangradefromlinq = dangradeselected.tolist();
   teammember.Dangrades.Add(dangradefromlinq[0]);
   }

If you had previously stored dangrades in some other format (ie not a database) then you will have to append your code, or ask specifically with that for a better answer.

Also don't forget to give your select and id= (lookup html attributes) so that the controller can recognize it.

You can probably make this (pseudo)code a little neater. Also don't forget about possible null values.

If you want to save a list of items for each teamember you can also look into having 2 databases. I'm not sure if this is recommended. But you can have one for teammembers, and one for dangrades. In the case of dangrades you would add an additional property called grouping id that would match the id of your teammember. So when you pull up your teammember you could also pull up all related dawngrades that match its database id.

That's everything I can think of. If you find a simpler solution by all means go with that.

Upvotes: 0

Mathieu VIALES
Mathieu VIALES

Reputation: 4772

Shouldn't your model be like that ?

[UIHint("NameOfTheEditorTemplate")]
public virtual List<DanGrade> DanGrades { get; set; }

Be sure to put the EditorTemplate under one of these two paths

~/Views/Shared/EditorTemplates
~/Views/Controller_Name/EditorTemplates

As explained in this post

Upvotes: 0

Related Questions