Hello everybody. Faced a problem: in the application I need to add the product to the database, while it is not known in advance how many different descriptions (characteristics) it will have on this there are 2 tables - Products and Characteristics. When adding a new product, you must send to the server the product form and a collection of characteristics. I made a general model for this. But when I click submit and check the dev tools, there I see that the product model is being sent, and there are no close specifications. Where is my mistake? Below lay the listing of models, server code and view.

Product Model:

public partial class bs_parts { public bs_parts() { this.bs_details = new HashSet<bs_details>(); this.bs_images = new HashSet<bs_images>(); this.bs_orders = new HashSet<bs_orders>(); } public decimal parts_id { get; set; } public decimal parts_category_id { get; set; } public decimal parts_brand_id { get; set; } public string parts_model { get; set; } public double parts_price { get; set; } public int parts_amount { get; set; } public virtual bs_brands bs_brands { get; set; } public virtual bs_categories bs_categories { get; set; } public virtual ICollection<bs_details> bs_details { get; set; } public virtual ICollection<bs_images> bs_images { get; set; } public virtual ICollection<bs_orders> bs_orders { get; set; } } 

Feature Model:

 public partial class bs_details { public decimal details_id { get; set; } public string details_name { get; set; } public string details_value { get; set; } public decimal details_part_id { get; set; } public virtual bs_parts bs_parts { get; set; } } 

And the general model:

 public class AddPartViewModel { public bs_parts part { get; set; } public List<bs_details> detail { get; set; } public bs_images image { get; set; } } 

Server side code:

 [HttpPost] public ActionResult CreatePart(AddPartViewModel model, HttpPostedFileBase uploadImage) { ViewBag.Categories = new SelectList(_db.bs_categories, "categories_id", "categories_name"); ViewBag.Brands = new SelectList(_db.bs_brands, "brands_id", "brands_name"); if (ModelState.IsValid && model.part != null) { model.part.parts_brand_id = 3; model.part.parts_category_id = 3; _db.bs_parts.Add(model.part); if (model.detail != null) { foreach (var details in model.detail) { details.details_part_id = 8; _db.bs_details.Add(details); _db.SaveChanges(); } } if (ModelState.IsValid && uploadImage!=null) { byte[] imageData = null; using (var binaryReader = new BinaryReader(uploadImage.InputStream)) { imageData = binaryReader.ReadBytes(uploadImage.ContentLength); } model.image.image_part_id = model.part.parts_id; model.image.images_image = imageData; _db.bs_images.Add(model.image); } //добавить else if () {...} если модель есть, а изображения нет _db.SaveChanges(); return RedirectToAction("Main"); } return View(model); } 

View:

 <div> <p> Выбрать категорию: @Html.DropDownList("Categories"); </p> </div> <div> <p> Выбрать производителя: @Html.DropDownList("Brands"); </p> </div> <div> <p> Название модели: @Html.EditorFor(model=>model.part.parts_model) @Html.ValidationMessageFor(model=>model.part.parts_model) </p> </div> <div> <p> Цена: @Html.EditorFor(model=>model.part.parts_price) @Html.ValidationMessageFor(model=>model.part.parts_price) </p> </div> <div> <p> Количество: @Html.EditorFor(model=>model.part.parts_amount) @Html.ValidationMessageFor(model=>model.part.parts_amount) </p> </div> <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script> <form id="myForm" method="post" action="~/Controllers/AdminController.cs"> <div class="details"> <div class="details-part"> <input class="details_name" type="text" /> <input class="details_value" type="text" /> </div> </div> <div><p><a class="plus">Add detail</a></p></div> <div> <input type="file" name="uploadImage" /> </div> <input type="hidden" name="details"/> <div> <button id="save-form">Save</button> </div> </form> 

Script to send and add new fields:

 <script> var $myForm = $("#myForm"); $(".plus").off("click").on("click", function () { $(".details").append('<div class="details-part"> <input class="details_name" type="text" /> <input class="details_value" type="text" /> </div>') }); $("#save-form").off("click").on("click", function () { var detailList = []; $myForm.find(".details-part").each(function () { detailList.push({ details_value: $(this).find(".details_value").val(), details_name: $(this).find(".details_name").val() }) }); $myForm.find('[name="details"]').val(JSON.stringify(detailList)) $myForm.submit() }); 

In this version, detail comes null, but if you change it and do it like this:

  <input name="detail[0].details_name" type="text" /> <input name="detail[0].details_value" type="text" /> <input name="detail[1].details_name" type="text" /> <input name="detail[1].details_value" type="text" /> <input name="detail[2].details_name" type="text" /> <input name="detail[2].details_value" type="text" /> 

That comes to the server 3 detail objects and everything is ok. The problem will be, if I want to remove the object in the middle, then the order will be broken. I have already been told that you can not delete, but make the fields hidden, and on the server already do a check for an empty field. I am not in JS with a foot, maybe someone who has free time and a desire can help write a script such that it adds new fields and makes them hidden when "deleted". With the addition I can handle, but with the second problem.

  • I understand that you have AddPartViewModel - it is used on the view. And when you open the page, do you have a detail in the model accidentally not null ? - Denis Bubnov
  • @DenisBubnov detail is null, and the part model comes in. I can manually send it when I use such fields with identifiers <input name = "detail [0] .details_name" type = "text" /> But if the script adds a few of these, but then I want to remove something from the middle, the order will be broken - C. America

1 answer 1

for the case when you generate inputs for a list of objects, and do not guarantee a sequence of indexes (for example, when deleting some elements), you can use the index property:

 <input name="detail.Index" type="hidden" value="0"/> <input name="detail[0].details_name" type="text" /> <input name="detail[0].details_value" type="text" /> <input name="detail.Index" type="hidden" value="1"/> <input name="detail[1].details_name" type="text" /> <input name="detail[1].details_value" type="text" /> <input name="detail.Index" type="hidden" value="2"/> <input name="detail[2].details_name" type="text" /> <input name="detail[2].details_value" type="text" /> 

with the use of this field all data will be included in your model, regardless of the omission of numbers. In general, your indexes may not even be numeric, for example:

 <input name="detail.Index" type="hidden" value="test"/> <input name="detail[test].details_name" type="text" /> <input name="detail[test].details_value" type="text" />