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:
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: