Home > Software design >  how to add multiple selected option value in list of one-to-many relationship class
how to add multiple selected option value in list of one-to-many relationship class

Time:04-01

This is "Item" class

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace RandApp.Models
{
    public class Item : BaseEntity
    {
        [Required]
        [Display(Name = "Item Photo")]
        public string ItemPhoto { get; set; }
        [Required]
        [Display(Name = "Item Type")]
        public string ItemType { get; set; }
        [Required]
        [Display(Name = "Item Category")]
        public string ItemCategory { get; set; }
        [Required]
        [Display(Name = "Item Name")]
        public string Name { get; set; }
        [Required]
        [Display(Name = "Item Colors")]
        public List<ItemColors> Color { get; set; } = new List<ItemColors>();
        [Required]
        [Display(Name = "Item Size")]
        public List<ItemSizes> Size { get; set; } = new List<ItemSizes>();
        [Required]
        [Display(Name = "Item Material Type")]
        public string MaterialType { get; set; }
        [Required]
        [Display(Name = "Designed For")]
        public string DesignedFor { get; set; }
        [Required]
        [Display(Name = "Item Price")]
        public double Price { get; set; }
        [Required]
        [Display(Name = "Item Description")]
        public string Description { get; set; }
    }
}

This is "ItemColors" and "ItemSizes" classes

using System.ComponentModel.DataAnnotations.Schema;

namespace RandApp.Models
{
    public class ItemColors : BaseEntity
    {
        public string ItemColor { get; set; }
        public int? ItemId { get; set; }
        [ForeignKey("ItemId")]
        public Item Item { get; set; }
    }
}

using System.ComponentModel.DataAnnotations.Schema;

namespace RandApp.Models
{
    public class ItemSizes : BaseEntity
    {
        public string ItemSize { get; set; }
        public int? ItemId { get; set; }
        [ForeignKey("ItemId")]
        public Item Item { get; set; }
    }
}

and This is the view

@model RandApp.Models.Item

<h4>Add Item</h4>
<hr />
<div >
    <div >
        <form asp-action="CreateItem" enctype="multipart/form-data">
            <div >
                <label asp-for="@Model.ItemPhoto" ></label>
                <input type="file" name="ItemPhoto"  onchange="SetValue(this)" />
                <input asp-for="@Model.ItemPhoto" hidden />
                <span asp-validation-for="@Model.ItemPhoto" ></span>
            </div>
            <div >
                <label asp-for="@Model.Name" ></label>
                <input asp-for="@Model.Name"  />
                <span asp-validation-for="@Model.Name" ></span>
            </div>
            <div >
                <label asp-for="@Model.DesignedFor" ></label>
                <select asp-for="@Model.DesignedFor" >
                    <option value="" selected>Select Designed For</option>
                    @foreach (var desigendFor in Enum.GetValues(typeof(RandApp.Enums.DesignedFor)))
                    {
                        <option val="@desigendFor" value="@desigendFor.ToString()">@desigendFor</option>
                    }
                </select>
                <span asp-validation-for="@Model.DesignedFor" ></span>
            </div>
            <div >
                <label asp-for="@Model.ItemCategory" ></label>
                <select asp-for="@Model.ItemCategory" >
                </select>
                <span asp-validation-for="@Model.ItemCategory" ></span>
            </div>
            <div >
                <label asp-for="@Model.ItemType" ></label>
                <select asp-for="@Model.ItemType" >
                </select>
                <span asp-validation-for="@Model.ItemType" ></span>
            </div>
            <div >
                <label asp-for="@Model.MaterialType" ></label>
                <select asp-for="@Model.MaterialType" >
                    <option value="" selected>Select Material Type</option>
                    @foreach (var materialType in Enum.GetValues(typeof(RandApp.Enums.MaterialType)))
                    {
                        <option value="@materialType.ToString()">@materialType</option>
                    }
                    <option value="Other">Other</option>
                </select>
                <input id="otherMaterialInp" asp-for="@Model.MaterialType"  style="display:none; margin-top:8px" placeholder="Enter Material Type" />
                <span asp-validation-for="@Model.MaterialType" ></span>
            </div>
            <div >
                <label asp-for="@Model.Color" ></label>
                <select asp-for="@Model.Color" multiple >
                    @foreach (var color in Enum.GetValues(typeof(RandApp.Enums.ItemColor)))
                    {
                        <option value="@color.ToString()">@color.ToString()</option>
                    }
                    <option value="Other">Other</option>
                </select>
                <input id="otherColorInp" asp-for="@Model.Color"  style="display:none; margin-top:8px" placeholder="Enter Item Color" />
                <span asp-validation-for="@Model.Color" ></span>
            </div>
            <div >
                <label asp-for="@Model.Size" ></label>
                <select asp-for="@Model.Size" multiple >
                </select>
                <span asp-validation-for="@Model.Size" ></span>
            </div>
            <div >
                <label asp-for="@Model.Price" ></label>
                <input asp-for="@Model.Price"  />
                <span asp-validation-for="@Model.Price" ></span>
            </div>
            <div >
                <label asp-for="@Model.Description" ></label>
                <textarea asp-for="@Model.Description" ></textarea>
                <span asp-validation-for="@Model.Description" ></span>
            </div>
            <div >
                <input type="submit" value="Create" asp-for-  />
            </div>
        </form>
    </div>
</div>

So this is what I want to do: I want to select multiple color and size and add them values in Item.Color and Item.Size lists, but when I try it, I get empty lists in controller. There is controller

// POST Create
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> CreateItem(Item item, IFormFile ItemPhoto)
{
     if (!ModelState.IsValid)
     {
         return View();
     }

     var path = Path.Combine(_webHostEnvironment.WebRootPath, "assets", ItemPhoto.FileName);
     var stream = new FileStream(path, FileMode.Create);
     await ItemPhoto.CopyToAsync(stream);
     item.ItemPhoto = ItemPhoto.FileName;

     await _itemRepo.CreateAsync(item);
     stream.Close();
     return RedirectToAction("Index");
}

How can I get multiple values from dropdown list and add them in lists? When I try to console selected values with JavaScript code, it prints correct values, but the problem is that, I can send them to controller and save them in db

CodePudding user response:

Model Binding binds the property by name. For your multiple selection, they match the model property in List<ItemColors> Color/List<ItemSizes> Size. So when you pass the selected value, the name should be something like:Color[index].ItemColor/Size[index].ItemSize.

Not sure how is your js code like, no need add additional input elements for the select in my demo.

<input id="otherMaterialInp" asp-for="@Model.MaterialType"  style="display:none; margin-top:8px" placeholder="Enter Material Type" />
<input id="otherColorInp" asp-for="@Model.Color"  style="display:none; margin-top:8px" placeholder="Enter Item Color" />

My whole working demo:

Model class:

public class BaseEntity
{
    public int Id { get; set; }
}
public class Item : BaseEntity
{
    [Display(Name = "Item Photo")]
    public string ItemPhoto { get; set; }
    [Display(Name = "Item Type")]
    public string ItemType { get; set; }      
    [Display(Name = "Item Category")]
    public string ItemCategory { get; set; }
    [Display(Name = "Item Name")]
    public string Name { get; set; }
    [Display(Name = "Item Colors")]
    public List<ItemColors> Color { get; set; } = new List<ItemColors>();
    [Display(Name = "Item Size")]
    public List<ItemSizes> Size { get; set; } = new List<ItemSizes>();
    [Display(Name = "Item Material Type")]
    public string MaterialType { get; set; }
    [Display(Name = "Designed For")]
    public string DesignedFor { get; set; }
    [Display(Name = "Item Price")]
    public double Price { get; set; }
    [Display(Name = "Item Description")]
    public string Description { get; set; }
}
public class ItemColors : BaseEntity
{
    public string ItemColor { get; set; }
    public int? ItemId { get; set; }
    [ForeignKey("ItemId")]
    public Item Item { get; set; }
}
public class ItemSizes : BaseEntity
{
    public string ItemSize { get; set; }
    public int? ItemId { get; set; }
    [ForeignKey("ItemId")]
    public Item Item { get; set; }
}

Enum:

public enum MaterialType
{
    Meterial1,
    Meterial2,
    Meterial3
}
public enum ItemColor
{
    Red,
    Blue,
    Green
}
public enum DesignedFor
{
    Student,
    Teacher,
    Normal
}

View:

@model Item

<div >
    <div >
        <form asp-action="CreateItem" enctype="multipart/form-data">
            <div >
                <label asp-for="@Model.ItemPhoto" ></label>
                <input type="file" name="ItemPhoto"  onchange="SetValue(this)" />
                <input asp-for="@Model.ItemPhoto" hidden />
                <span asp-validation-for="@Model.ItemPhoto" ></span>
            </div>
            <div >
                <label asp-for="@Model.Name" ></label>
                <input asp-for="@Model.Name"  />
                <span asp-validation-for="@Model.Name" ></span>
            </div>
            <div >
                <label asp-for="@Model.DesignedFor" ></label>
                <select asp-for="@Model.DesignedFor" >
                    <option value="" selected>Select Designed For</option>
                    @foreach (var desigendFor in Enum.GetValues(typeof(DesignedFor)))
                    {
                        <option val="@desigendFor" value="@desigendFor.ToString()">@desigendFor</option>
                    }
                </select>
                <span asp-validation-for="@Model.DesignedFor" ></span>
            </div>
            <div >
                <label asp-for="@Model.ItemCategory" ></label>
                <select asp-for="@Model.ItemCategory" >
                    <option value="Cat1">Cat1</option>
                    <option value="Cat2">Cat2</option>
                    <option value="Cat3">Cat3</option>
                </select>
                <span asp-validation-for="@Model.ItemCategory" ></span>
            </div>
            <div >
                <label asp-for="@Model.ItemType" ></label>
                <select asp-for="@Model.ItemType" >
                    <option value="Item1">Item1</option>
                    <option value="Item2">Item2</option>
                    <option value="Item3">Item3</option>
                </select>
                <span asp-validation-for="@Model.ItemType" ></span>
            </div>
            <div >
                <label asp-for="@Model.MaterialType" ></label>
                <select asp-for="@Model.MaterialType" >
                    <option value="" selected>Select Material Type</option>
                    @foreach (var materialType in Enum.GetValues(typeof(MaterialType)))
                    {
                        <option value="@materialType.ToString()">@materialType</option>
                    }
                    <option value="Other">Other</option>
                </select>
 <!-- useless input   -->
                @*<input id="otherMaterialInp" asp-for="@Model.MaterialType"  style="display:none; margin-top:8px" placeholder="Enter Material Type" />*@
                <span asp-validation-for="@Model.MaterialType" ></span>
            </div>
            <div >
                <label asp-for="@Model.Color" ></label>
                <select asp-for="@Model.Color" multiple >
                    @foreach (var color in Enum.GetValues(typeof(ItemColor)))
                    {
                        <option value="@color.ToString()">@color.ToString()</option>
                    }
                    <option value="Other">Other</option>
                </select>
     <!-- useless input   -->
                @*<input id="otherColorInp" asp-for="@Model.Color"  style="display:none; margin-top:8px" placeholder="Enter Item Color" />*@
                <span asp-validation-for="@Model.Color" ></span>
            </div>
            <div >
                <label asp-for="@Model.Size" ></label>
                <select asp-for="@Model.Size" multiple >
                    <option value="1">1</option>
                    <option value="2">2</option>
                    <option value="3">3</option>
                </select>
                <span asp-validation-for="@Model.Size" ></span>
            </div>
            <div >
                <label asp-for="@Model.Price" ></label>
                <input asp-for="@Model.Price"  />
                <span asp-validation-for="@Model.Price" ></span>
            </div>
            <div >
                <label asp-for="@Model.Description" ></label>
                <textarea asp-for="@Model.Description" ></textarea>
                <span asp-validation-for="@Model.Description" ></span>
            </div>
            <div >    
                              <!-- change here   -->
                <input type="button" onclick="Create()" value="Create" asp-for-  />
            </div>
        </form>
    </div>
</div>

JS in my view:

@section Scripts {
    <script>
        function SetValue(el) {
            $("input:text#ItemPhoto").val($(el).val());
        }
        function Create () {           
            var fdata = new FormData();
            var fileInput = $("input:file")[0];
            var file = fileInput.files[0];
            fdata.append("ItemPhoto", file);

            fdata.append($("textarea").attr("name"), $("textarea").val());

            $("form input[type='text']").each(function (x, y) {
                fdata.append($(y).attr("name"), $(y).val());
            });
            $('select:not([multiple]) >option:selected').each(function () {
                var name = $(this).parent("select").attr("name");
                console.log(name);
                fdata.append(name, $(this).val());                   
            });
            $('select#Color >option:selected').each(function (index, obj) {
                fdata.append("Color["   index  "].ItemColor", $(this).val());
                console.log($(obj).val());
            });
            $('select#Size >option:selected').each(function (index, obj) {
                fdata.append("Size["   index  "].ItemSize", $(this).val());
                console.log($(obj).val());
            }); 
            var options = {};
            options.type = "POST";
            options.url = "/Items/CreateItem";
            options.dataType = "JSON";
            options.contentType = false;   
            options.processData = false;    
            options.data = fdata;
            options.beforeSend = function (xhr) {
                xhr.setRequestHeader("RequestVerificationToken", $('input:hidden[name="__RequestVerificationToken"]').val());
            };

            options.success = function (data) {
            };
            options.error = function (res) {
            };
            $.ajax(options);
        
        }
</script>
}

Controller:

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> CreateItem(Item item,IFormFile ItemPhoto)
{
    if (ModelState.IsValid)
    {  
       //be sure your wwwroot folder contains a folder named `assets`
       var path = Path.Combine(_webHostEnvironment.WebRootPath, "assets", ItemPhoto.FileName);
       var stream = new FileStream(path, FileMode.Create);
       await ItemPhoto.CopyToAsync(stream);
        item.ItemPhoto = ItemPhoto.FileName;
        _context.Add(item);
        await _context.SaveChangesAsync();
        return RedirectToAction(nameof(Index));
    }
    return View(item);
}

Result:

enter image description here


Another way is that just use form submit, you need set parameters for the multiple selection. Make <select asp-for="@Model.Color" multiple> as an example, you need add a type List<string> parameter named Color in backend.

Note: no need add the additional input for the multiple selection.

Whole working demo:

View:

@model Item

<div >
    <div >
        <form asp-action="CreateItem" enctype="multipart/form-data">
            <div >
                <label asp-for="@Model.ItemPhoto" ></label>
                <input type="file" name="ItemPhoto"  onchange="SetValue(this)" />
                <input asp-for="@Model.ItemPhoto" hidden />
                <span asp-validation-for="@Model.ItemPhoto" ></span>
            </div>
            <div >
                <label asp-for="@Model.Name" ></label>
                <input asp-for="@Model.Name"  />
                <span asp-validation-for="@Model.Name" ></span>
            </div>
            <div >
                <label asp-for="@Model.DesignedFor" ></label>
                <select asp-for="@Model.DesignedFor" >
                    <option value="" selected>Select Designed For</option>
                    @foreach (var desigendFor in Enum.GetValues(typeof(DesignedFor)))
                    {
                        <option val="@desigendFor" value="@desigendFor.ToString()">@desigendFor</option>
                    }
                </select>
                <span asp-validation-for="@Model.DesignedFor" ></span>
            </div>
            <div >
                <label asp-for="@Model.ItemCategory" ></label>
                <select asp-for="@Model.ItemCategory" >
                    <option value="Cat1">Cat1</option>
                    <option value="Cat2">Cat2</option>
                    <option value="Cat3">Cat3</option>
                </select>
                <span asp-validation-for="@Model.ItemCategory" ></span>
            </div>
            <div >
                <label asp-for="@Model.ItemType" ></label>
                <select asp-for="@Model.ItemType" >
                    <option value="Item1">Item1</option>
                    <option value="Item2">Item2</option>
                    <option value="Item3">Item3</option>
                </select>
                <span asp-validation-for="@Model.ItemType" ></span>
            </div>
            <div >
                <label asp-for="@Model.MaterialType" ></label>
                <select asp-for="@Model.MaterialType" >
                    <option value="" selected>Select Material Type</option>
                    @foreach (var materialType in Enum.GetValues(typeof(MaterialType)))
                    {
                        <option value="@materialType.ToString()">@materialType</option>
                    }
                    <option value="Other">Other</option>
                </select>
    <!-- useless input   -->
                @*<input id="otherMaterialInp" asp-for="@Model.MaterialType"  style="display:none; margin-top:8px" placeholder="Enter Material Type" />*@
                <span asp-validation-for="@Model.MaterialType" ></span>
            </div>
            <div >
                <label asp-for="@Model.Color" ></label>
                <select asp-for="@Model.Color" multiple >
                    @foreach (var color in Enum.GetValues(typeof(ItemColor)))
                    {
                        <option value="@color.ToString()">@color.ToString()</option>
                    }
                    <option value="Other">Other</option>
                </select>
     <!-- useless input   -->
                @*<input id="otherColorInp" asp-for="@Model.Color"  style="display:none; margin-top:8px" placeholder="Enter Item Color" />*@
                <span asp-validation-for="@Model.Color" ></span>
            </div>
            <div >
                <label asp-for="@Model.Size" ></label>
                <select asp-for="@Model.Size" multiple >
                    <option value="1">1</option>
                    <option value="2">2</option>
                    <option value="3">3</option>
                </select>
                <span asp-validation-for="@Model.Size" ></span>
            </div>
            <div >
                <label asp-for="@Model.Price" ></label>
                <input asp-for="@Model.Price"  />
                <span asp-validation-for="@Model.Price" ></span>
            </div>
            <div >
                <label asp-for="@Model.Description" ></label>
                <textarea asp-for="@Model.Description" ></textarea>
                <span asp-validation-for="@Model.Description" ></span>
            </div>
            <div >
                <input type="submit" value="Create" asp-for-  />
            </div>
        </form>
    </div>
</div>

JS in View:

@section Scripts {
    <script>
        function SetValue(el) {
            console.log($(el).val());
            $("input:text#ItemPhoto").val($(el).val());
        }       
</script>
}

Controller:

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> CreateItem(Item item,IFormFile ItemPhoto,List<string> Color,List<string> Size)
{
    foreach(var c in Color)
    {
        item.Color.Add(new ItemColors() { ItemColor = c });
    }
    foreach (var s in Size)
    {
        item.Size.Add(new ItemSizes() { ItemSize = s });
    }
    //......
    return View(item);
}

Result:

enter image description here

  • Related