Динамическое добавление полей и формирование JSON
Добрый день. Мне нужна помощь от знающих js, так как я полный наб.
Делаю форму добавления товара в интернет-магазин. Форма является частичным представлением на странице, по этому хочу использовать AJAX, чтобы добавлять поля динамически, без обновления страницы. <fieldset> <form> <legend>Добавить деталь</legend> <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> <div class="details"> <input type="text" name="details_name" /> <input type="text" name="details_value" /> </div> <div><p><a class="plus">Добавить описание</a></p></div> <div> <input type="file" name="uploadImage" /> </div> </form> <div> <input type="submit" value="Сохранить" /> </div> </fieldset> После нажатия на "Добавить описание", необходимо добавить 2 поля, аналогичные классу "details". После submit, соответственно сформировать json и отправить на сервер. Не знаю, можно ли сформировать JSON из Razor элементов, может по другому как-то сделать, может обойтись без JSON. Получалось добавлять товар, вручную задавая количество полей и задавая им индексы: <div> <p> Название характеристики: Html.EditorFor(model=>model.detail[0].details_name) </p> </div> <div> <p> Значение: Html.EditorFor(model=>model.detail[0].details_value) </p> </div> Пытаюсь добавлять поля таким образом: $(function() { $('form').on('click', 'a.plus', function() { var fld = $(this).closest('div').prev().find('div.details').last(), add=fld.clone().val(''); fld.after(add)}) }) |
В общем добавляю таким образом:
var timer=0; $(".plus").off("click").on("click",function(){ timer++; $(".details").append(` <input class="copy-input" type="text" name="details_name${timer}" /> <input class="copy-input" type="text" name="details_value${timer}" />`) }) разметку чуть изменил: <div class="details"> <input class="copy-input" type="text" name="details_name" /> <input class="copy-input" type="text" name="details_value" /> </div> Осталось как-то отправить это все дело на сервер. |
fieldset является элементом формы, а не наоборот.
|
laimas,
спасибо за замечание, поправил |
Ну а об остальном, так это сначала нужно задаться вопросом "что такое товар" и только затем как его добавлять.
Товары описаны в базе, и это несколько основных полей - наименование, цена, описание. С этой таблицей могут быть связаны таблицы "Производитель", "Характеристики", "Изображения" и т.п. Это значит, что при добавлении товара сервер возвращает готовую форму, а речь о добавлении поля для формы может возникнуть тогда, когда например товар имеет характеристику, которая не описана в базе. Но в этом случае нужно добавлять новую характеристику в таблицу "Характеристики" (редактирование таблицы), а не в форму добавления товара. С какой стати возникает потребность добавления поля описания товара, которое уже должно быть в форме? |
laimas,
Вы все верно описали, так и есть. У меня таблица Товаров связана с таблицами "Категории", "Производители", и "Изображения". А динамически добавляются поля, для таблицы "Характеристики". Добавлять новые поля получилось. Теперь проблема в том, что при отправлении на сервер, эти новые поля до него не доходят. Я так понимаю, что необходимо сформировать JSON объект, но не получается. |
Если говорить о добавлении характеристик во время добавления товара, то добавление должно быть в случае если в базе таковых нет. Это означает, что в форме есть список имеющихся характеристик и кнопка "Добавить новую" или подобная. Такого в вашей форме не наблюдается, да и такое добавление должно базироваться на проверке сервером добавляемых характеристик налету, так как само добавление товара может порождать ошибки, а значит сам диалог клиент-сервер обрабатывающий примем формы будет не простым. То есть вполне возможны ситуации когда могут появится несвязанные данные. Но если прием построен по уму, можно и так, вот только name="details_name", это как посмотреть, все зависит от серверного языка, так что вполне можно получить последнюю даже если добавлялась дюжина.
Серверу для приема формы никакого JSON не требуется, отправляя форму естественным образом сервер получает же не JSON. Код отправки формы покажите. |
laimas,
на id не обращайте внимание, это для проверки просто [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); } Вот что сейчас приходит на сервер: ![]() |
У вас используется ASP .NET?
|
laimas,
да |
laimas,
у меня работала отправка формы, когда я делал так: Html.EditorFor(model=>model.detail[0].details_name) Html.EditorFor(model=>model.detail[0].details_value) но Razor нельзя добавлять динамически |
Я не знаю ASP .NET на таком уровне, чтобы оказывать какую либо существенную помощь кому либо. :) И к JS это не имеет отношения, нужно было размещать тему в другом разделе, вполне возможно что кто-то и занимается им серьезно.
|
Цитата:
<input class="copy-input" type="text" name="details_name[]" /> <input class="copy-input" type="text" name="details_value[]" /> |
Цитата:
|
Цитата:
if (model.detail != null) { foreach (var details in model.detail) чем в данном случае является model.detail? Это должна быть коллекция элементов именно с именем details_name (при этом где details_value?). |
laimas,
у меня есть общая модель для таблиц Товара, Характеристик и Изображения. model.detail это я так обращаюсь к модели details. То есть мне на сервер приходит общая модель, содержащая в себе модель Товара(название, категория, бренд и т.д.), модель Характеристик(коллекция {название+значение}) и Изображение(хранится в двоичных данных). После того как эта модель пришла, я по очереди "вытягиваю" из нее эти три модели и добавляю в бд. |
И что возвращает модель? Проверить то это можно. Вы ведь пытаетесь обойти объект model.detail циклом чтобы получить его элементы. Так что вы получаете?
|
laimas,
да можно, я прикреплял в каком-то посте скрин. Там null. part приходит нормально, image я не загружаю, а вот detail не хочет и все =/ ![]() |
Цитата:
|
laimas,
пока без Ajax <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() }); </script> |
И какой смысл в этом коде? Во-первых по коду выше у вас нет полей с классами details_value и details_name, это имена полей, поэтому $(this).find(".details_value") ничего не вернет.
Во-вторых, после получения данных полей и формирования JSON нужно эти поля удалять из формы. В третьих, ваша модель должна ожидать JSON, декодировать его. Вот только какой в этом смысл, если без такого преобразования остальные поля формы передаются же серверу, разве нельзя отправить просто набор и полей характеристик? |
что я только что заметил, так это то, что в dev tools мои отправляемые данные все есть, но до сервера почему-то не доходят
![]() |
Вот именно, поля формы можно отправить как есть, без всяких json преобразований. Вопрос только в том, что ожидает сервер и как эти данные обрабатывает.
|
Все таки проблема в отправке. Вышло отправить "ручками" на сервер данные в таком виде:
<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" /> А если я захочу удалить какой-то из добавленных? Есть способы какие-то? |
Проблема не в отправке, а в именовании полей формы: а) вашим контроллером они оговаривают и ожидаются; б) именоваться поля должны как элементы массива, при этом жестко указывать индекс возможно и не надо.
Можно конечно удалять, только у вас нужно удалять пару. Разберитесь сначала с именованием, вот так будет приниматься массив: <input name="detail[].details_name" type="text" /> <input name="detail[].details_value" type="text" /> .... На засыпку - а какие характеристики описываются? |
Цитата:
Характеристики разные, допустим велосипедная рама:вес, цвет, углы различные и т.д. |
Цитата:
Например в РНР я могу сам разбирать RAW данные, но проще использовать готовое, которое обеспечивается языком - в нем данные формы попадут в массив $_POST (отправка файлов только методом POST), а информация о загруженных файлах в массив $_FILES. При этом парсер будет поступать так - если к примеру отправляется три поля формы с именами "а", то в массиве окажется только одно значение, последнего поля. И это логично - массив не может иметь одинаковых индексов/ключей, а предписанием парсеру получить все поля является указание принадлежности имен к массиву "а[], "а[]", "а[]". В этом случае парсер автоматически поместит значения полей в массив "а" соответственно под индексами 0, 1, 2. То есть указывать жестко индексы в именовании не требуется. Такое именование позволяет получать на сервере вложения элементов формы (группировка) определяемые разработчиком самой формой, то есть многомерные массивы. А необязательность указания индекса позволяет легче оперировать элементами на клиенте, например тогда, когда серверу необходима последовательность индексов от 0 до ... К примеру с вашей жесткой индексацией при удалении произвольных индексов (полей) как поведет себя контроллер? Исходя из этого условия в среде РНР нельзя именовать выпадающий список со множественным выбором не указывая его имя как "name[]", иначе разработчик получит значение только последней выбранной в списке опции. Похоже этим же условием руководствуется и ваш контроллер. Проверьте не будет ли ошибок с такими полями: <input name="detail[3].details_name" type="text" /> <input name="detail[3].details_value" type="text" /> <input name="detail[8].details_name" type="text" /> <input name="detail[8].details_value" type="text" /> У вас нужно добавлять и удалять по паре полей, и реальный код такого удаления будет сильно зависеть от html-структуры. Надо сначала с этим определиться, можно конечно и так поступить: <input name="detail[0].details_name" type="text" /> <input name="detail[0].details_value" type="text" /> <input type="button" value="Удалить" /> <input name="detail[1].details_name" type="text" /> <input name="detail[1].details_value" type="text" /> <input type="button" value="Удалить" /> Но такая структура не удобна ведь с точки зрения информативности. Тут либо каждые два поля + кнопка удаления в одну линию, либо вложенные в родительский элемент имеющий визуальные границы. Подумайте сначала над этим. Цитата:
|
Цитата:
|
Цитата:
"Колесо", "100 руб.", "ООО Автозапчасть", "1.jpg" "Колесо", "100 руб.", "ООО Автозапчасть", "2.jpg" "Колесо", "100 руб.", "ООО Автозапчасть", "3.jpg" .... Да, все в одной таблице, но при этом объем этой таблицы неоправданно возрастает из-за избыточных данных - повторяющихся описаний основных характеристик товара. Тоже самое ожидается и вашей таблице характеристик, так как, например, характеристику "цвет" могут иметь все ваши запчасти, и ее значение "черный" также. А по уму основная таблица должна иметь связь с таблицей описывающей уже определенные характеристики (набор характеристик) через внешнюю таблицу связей. Если такой набор фиксированный, динамически неизменяемый, то выгоднее такие характеристики как цвет, вес, размер, (их значения) описывать в основной таблице. При проектировании баз данных руководствуются принципами нормализации: https://support.microsoft.com/ru-ru/...ization-basics https://habrahabr.ru/post/254773/ |
Принимает ваш контроллер произвольные индексы (по идее должен)?
|
Цитата:
model.part.parts_brand_id = 3; model.part.parts_category_id = 3; так это просто для теста |
Цитата:
Код:
model.part.parts_brand_id = 3; |
упс, лагануло
|
Индексы, это указанные в именах полей
detail[3].details_name - здесь 3 Ваш контроллер примет без проблем их произвольный порядок? |
нет, если где-то в индексах идет перескок, то он дальше не фурычит
|
dev tools в браузере видит все поля, а контроллер принимает только те, что идут по порядку, до прерывания
|
Если контроллер нифига не понимает коллекции с произвольной индексацией, то это плохо. Либо вы переписываете его код "до умного", либо при удалении добавленных полей потребуется потрошить оставшиеся, меня в них индексы от 0 до ...
|
Есть вариант не удалять поля, а делать их hidden, а в контроллере проверку замутить
|
А причем тут hidden? Кроме имени и значения поля, другие его свойства на сервер не передаются.
Проще переписать контроллер, если это затруднительно, то выгоднее добавить индексацию полям при отправке формы, а до этого именовать как detail.details_name и и.д. Тем более если форму отправлять асинхронным запросом. Но, как уже отмечалось, вашем случае иметь такие характеристики как цвет, вес, размер... не выгодно в отдельной таблице - не может быть цвет одновременно быть и черным, и белым. Подумайте сначала над этим, затем над html-структурой формы, после чего можно будет писать код добавления/удаления полей формы (если только это потребуется), и ее ajax отправке. |
Я в одном большом интернет-магазине увидел одну и ту же группу товаров(раму велосипедную) и у этих товаров были разные характеристики. У какого-то товара указывались размеры, у другого - еще что-то, то есть не было жестко фиксированных полей, я по этому думал так сделать...но сейчас вы заставили меня задуматься, правильно ли я решил делать.
|
Часовой пояс GMT +3, время: 19:47. |