Javascript-форум (https://javascript.ru/forum/)
-   Элементы интерфейса (https://javascript.ru/forum/dom-window/)
-   -   Динамическое добавление полей и формирование JSON (https://javascript.ru/forum/dom-window/68690-dinamicheskoe-dobavlenie-polejj-i-formirovanie-json.html)

Scantraxx 03.05.2017 09:31

Динамическое добавление полей и формирование 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)})
    })

Scantraxx 03.05.2017 10:35

В общем добавляю таким образом:
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>


Осталось как-то отправить это все дело на сервер.

laimas 03.05.2017 10:37

fieldset является элементом формы, а не наоборот.

Scantraxx 03.05.2017 10:46

laimas,
спасибо за замечание, поправил

laimas 03.05.2017 11:04

Ну а об остальном, так это сначала нужно задаться вопросом "что такое товар" и только затем как его добавлять.

Товары описаны в базе, и это несколько основных полей - наименование, цена, описание. С этой таблицей могут быть связаны таблицы "Производитель", "Характеристики", "Изображения" и т.п.

Это значит, что при добавлении товара сервер возвращает готовую форму, а речь о добавлении поля для формы может возникнуть тогда, когда например товар имеет характеристику, которая не описана в базе. Но в этом случае нужно добавлять новую характеристику в таблицу "Характеристики" (редактирование таблицы), а не в форму добавления товара.

С какой стати возникает потребность добавления поля описания товара, которое уже должно быть в форме?

Scantraxx 03.05.2017 11:55

laimas,
Вы все верно описали, так и есть. У меня таблица Товаров связана с таблицами "Категории", "Производители", и "Изображения". А динамически добавляются поля, для таблицы "Характеристики".
Добавлять новые поля получилось. Теперь проблема в том, что при отправлении на сервер, эти новые поля до него не доходят. Я так понимаю, что необходимо сформировать JSON объект, но не получается.

laimas 03.05.2017 14:38

Если говорить о добавлении характеристик во время добавления товара, то добавление должно быть в случае если в базе таковых нет. Это означает, что в форме есть список имеющихся характеристик и кнопка "Добавить новую" или подобная. Такого в вашей форме не наблюдается, да и такое добавление должно базироваться на проверке сервером добавляемых характеристик налету, так как само добавление товара может порождать ошибки, а значит сам диалог клиент-сервер обрабатывающий примем формы будет не простым. То есть вполне возможны ситуации когда могут появится несвязанные данные. Но если прием построен по уму, можно и так, вот только name="details_name", это как посмотреть, все зависит от серверного языка, так что вполне можно получить последнюю даже если добавлялась дюжина.

Серверу для приема формы никакого JSON не требуется, отправляя форму естественным образом сервер получает же не JSON. Код отправки формы покажите.

Scantraxx 03.05.2017 14:59

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);
        }


Вот что сейчас приходит на сервер:

laimas 03.05.2017 15:10

У вас используется ASP .NET?

Scantraxx 03.05.2017 15:16

laimas,
да

Scantraxx 03.05.2017 15:18

laimas,
у меня работала отправка формы, когда я делал так:

Html.EditorFor(model=>model.detail[0].details_name)
Html.EditorFor(model=>model.detail[0].details_value)


но Razor нельзя добавлять динамически

laimas 03.05.2017 15:21

Я не знаю ASP .NET на таком уровне, чтобы оказывать какую либо существенную помощь кому либо. :) И к JS это не имеет отношения, нужно было размещать тему в другом разделе, вполне возможно что кто-то и занимается им серьезно.

laimas 03.05.2017 15:24

Цитата:

Сообщение от Scantraxx
у меня работала отправка формы, когда я делал так

Попробуйте так именовать поля характеристик:

<input class="copy-input" type="text" name="details_name[]" />
<input class="copy-input" type="text" name="details_value[]" />

Scantraxx 03.05.2017 15:36

Цитата:

Сообщение от laimas (Сообщение 451523)
Попробуйте так именовать поля характеристик:

не помогло:( я вот пробую просто без добавления новых полей отправить форму, а все равно null. Спасибо за внимание, буду копаться

laimas 03.05.2017 15:45

Цитата:

Сообщение от Scantraxx
не помогло

Ожидаемо. На клиенте собственно фиолетово как поля дразнятся, в том же JS это просто набор элементов формы. А вот как получает их сервер, тут уже не все однозначно.

if (model.detail != null) {
foreach (var details in model.detail)

чем в данном случае является model.detail? Это должна быть коллекция элементов именно с именем details_name (при этом где details_value?).

Scantraxx 03.05.2017 15:56

laimas,
у меня есть общая модель для таблиц Товара, Характеристик и Изображения. model.detail это я так обращаюсь к модели details.
То есть мне на сервер приходит общая модель, содержащая в себе модель Товара(название, категория, бренд и т.д.), модель Характеристик(коллекция {название+значение}) и Изображение(хранится в двоичных данных). После того как эта модель пришла, я по очереди "вытягиваю" из нее эти три модели и добавляю в бд.

laimas 03.05.2017 15:59

И что возвращает модель? Проверить то это можно. Вы ведь пытаетесь обойти объект model.detail циклом чтобы получить его элементы. Так что вы получаете?

Scantraxx 03.05.2017 16:07

laimas,
да можно, я прикреплял в каком-то посте скрин. Там null. part приходит нормально, image я не загружаю, а вот detail не хочет и все =/

laimas 03.05.2017 16:17

Цитата:

Сообщение от Scantraxx
part приходит нормально

Я не о приходе говорю, а о объекте на клиенте и о коде отправки формы спрашивал, а не о об обработчике ее на сервере. Вы же об Ajax отправке речь вели, форма ваша асинхронно у вас отравляется?

Scantraxx 03.05.2017 16:21

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>

laimas 03.05.2017 16:32

И какой смысл в этом коде? Во-первых по коду выше у вас нет полей с классами details_value и details_name, это имена полей, поэтому $(this).find(".details_value") ничего не вернет.

Во-вторых, после получения данных полей и формирования JSON нужно эти поля удалять из формы.

В третьих, ваша модель должна ожидать JSON, декодировать его. Вот только какой в этом смысл, если без такого преобразования остальные поля формы передаются же серверу, разве нельзя отправить просто набор и полей характеристик?

Scantraxx 03.05.2017 16:32

что я только что заметил, так это то, что в dev tools мои отправляемые данные все есть, но до сервера почему-то не доходят

laimas 03.05.2017 16:40

Вот именно, поля формы можно отправить как есть, без всяких json преобразований. Вопрос только в том, что ожидает сервер и как эти данные обрабатывает.

Scantraxx 04.05.2017 10:57

Все таки проблема в отправке. Вышло отправить "ручками" на сервер данные в таком виде:
<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" />


А если я захочу удалить какой-то из добавленных? Есть способы какие-то?

laimas 04.05.2017 11:24

Проблема не в отправке, а в именовании полей формы: а) вашим контроллером они оговаривают и ожидаются; б) именоваться поля должны как элементы массива, при этом жестко указывать индекс возможно и не надо.

Можно конечно удалять, только у вас нужно удалять пару. Разберитесь сначала с именованием, вот так будет приниматься массив:

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


На засыпку - а какие характеристики описываются?

Scantraxx 04.05.2017 11:52

Цитата:

Сообщение от laimas (Сообщение 451583)
вот так будет приниматься массив:

не хочет он так приниматься, возможно потому, что я на в ходе в контроллер принимаю модель, а не коллекцию

Характеристики разные, допустим велосипедная рама:вес, цвет, углы различные и т.д.

laimas 04.05.2017 12:36

Цитата:

Сообщение от Scantraxx
не хочет он так приниматься, возможно потому, что я на в ходе в контроллер принимаю модель, а не коллекцию

А вы в любом случае отправляет коллекцию, которая описывается согласно типу отправляемых данных enctype, который в вашем случае должен быть multipart/form-data. Серверный язык получая эти сырые данные (RAW) поступает так как ему предписано.

Например в РНР я могу сам разбирать 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="Удалить" />


Но такая структура не удобна ведь с точки зрения информативности. Тут либо каждые два поля + кнопка удаления в одну линию, либо вложенные в родительский элемент имеющий визуальные границы. Подумайте сначала над этим.

Цитата:

Сообщение от Scantraxx
вес, цвет, углы различные и т.д.

Походу получается, что ваша таблица характеристик будет страдать избыточностью.

Scantraxx 04.05.2017 13:14

Цитата:

Сообщение от laimas (Сообщение 451591)
Походу получается, что ваша таблица характеристик будет страдать избыточностью.

Ну я специально перед проектировкой архитектуры пошуршал инет и там был предложен именно такой способ. С удовольствием выслушаю советы, как сделать лучше. У меня интернет-магазин велозапчастей, там около 40+ категорий, а это, если разбивать все на отдельные таблицы, все те же 40+ новых таблиц, что выходит весьма емко.

laimas 04.05.2017 13:36

Цитата:

Сообщение от Scantraxx
если разбивать все на отдельные таблицы, все те же 40+ новых таблиц, что выходит весьма емко.

А почему тогда изображения у вас описываются в отдельной таблице, емко же? А если имена изображений помещать в основную таблицу, что получится? А получится следующее:

"Колесо", "100 руб.", "ООО Автозапчасть", "1.jpg"
"Колесо", "100 руб.", "ООО Автозапчасть", "2.jpg"
"Колесо", "100 руб.", "ООО Автозапчасть", "3.jpg"
....

Да, все в одной таблице, но при этом объем этой таблицы неоправданно возрастает из-за избыточных данных - повторяющихся описаний основных характеристик товара. Тоже самое ожидается и вашей таблице характеристик, так как, например, характеристику "цвет" могут иметь все ваши запчасти, и ее значение "черный" также.

А по уму основная таблица должна иметь связь с таблицей описывающей уже определенные характеристики (набор характеристик) через внешнюю таблицу связей. Если такой набор фиксированный, динамически неизменяемый, то выгоднее такие характеристики как цвет, вес, размер, (их значения) описывать в основной таблице.

При проектировании баз данных руководствуются принципами нормализации:

https://support.microsoft.com/ru-ru/...ization-basics
https://habrahabr.ru/post/254773/

laimas 04.05.2017 13:40

Принимает ваш контроллер произвольные индексы (по идее должен)?

Scantraxx 04.05.2017 13:50

Цитата:

Сообщение от laimas (Сообщение 451600)
Принимает ваш контроллер произвольные индексы (по идее должен)?

Вы за это говорите?
model.part.parts_brand_id = 3;
model.part.parts_category_id = 3;

так это просто для теста

Scantraxx 04.05.2017 13:51

Цитата:

Сообщение от laimas (Сообщение 451600)
Принимает ваш контроллер произвольные индексы (по идее должен)?

Вы за эти индексы говорите?
Код:

model.part.parts_brand_id = 3;
model.part.parts_category_id = 3;

Так это просто для теста сделал.

Scantraxx 04.05.2017 13:51

упс, лагануло

laimas 04.05.2017 13:58

Индексы, это указанные в именах полей

detail[3].details_name - здесь 3

Ваш контроллер примет без проблем их произвольный порядок?

Scantraxx 04.05.2017 14:09

нет, если где-то в индексах идет перескок, то он дальше не фурычит

Scantraxx 04.05.2017 14:12

dev tools в браузере видит все поля, а контроллер принимает только те, что идут по порядку, до прерывания

laimas 04.05.2017 14:20

Если контроллер нифига не понимает коллекции с произвольной индексацией, то это плохо. Либо вы переписываете его код "до умного", либо при удалении добавленных полей потребуется потрошить оставшиеся, меня в них индексы от 0 до ...

Scantraxx 04.05.2017 14:28

Есть вариант не удалять поля, а делать их hidden, а в контроллере проверку замутить

laimas 04.05.2017 14:35

А причем тут hidden? Кроме имени и значения поля, другие его свойства на сервер не передаются.

Проще переписать контроллер, если это затруднительно, то выгоднее добавить индексацию полям при отправке формы, а до этого именовать как detail.details_name и и.д. Тем более если форму отправлять асинхронным запросом.

Но, как уже отмечалось, вашем случае иметь такие характеристики как цвет, вес, размер... не выгодно в отдельной таблице - не может быть цвет одновременно быть и черным, и белым.

Подумайте сначала над этим, затем над html-структурой формы, после чего можно будет писать код добавления/удаления полей формы (если только это потребуется), и ее ajax отправке.

Scantraxx 04.05.2017 14:46

Я в одном большом интернет-магазине увидел одну и ту же группу товаров(раму велосипедную) и у этих товаров были разные характеристики. У какого-то товара указывались размеры, у другого - еще что-то, то есть не было жестко фиксированных полей, я по этому думал так сделать...но сейчас вы заставили меня задуматься, правильно ли я решил делать.


Часовой пояс GMT +3, время: 19:47.