jQuery plugin для форм с динамической структурой

    Хочу поделиться с обществом собственным плагином, который упрощает работу с динамическими формами и называется jqDynaForm. Под динамическими формами я подразумеваю формы, в которые при заполнении пользователь может добавлять по необходимости дополнительные поля или блоки полей. Разумеется, в каждом месте разрешено добавлять только заранее разрешенные виды блоков. Вот примеры подобных простейших форм:

    1. Контактная форма, в которой есть поле «телефон». Пользователь может добавить еще несколько дополнительных полей для телефонов, если возникнет желание.

    2. Счет на оплату. Есть фиксированный набор полей, таких как «имя плательщика» и «номер счета». Кроме того, есть таблица с позициями. Каждая позиция состоит следующих полей: «наименование», «количество», «цена». Пользователь может добавлять произвольное количество дополнительных позиций.

    Давайте рассмотрим работу с jqDynaForm на примере такого счета. Вот пример такой формы:





    Давайте посмотрим на HTML код, который необходимо приготовить для плагина:

    <!-- Base form -->
    <h1>Simple form demo</h1>
    <div id="smallForm">    
        <h2>Invoice</h2>
        <p>Number: <input name="number" size="6"> Payer: <input name="payer"></p>    
    
        <h3>Products</h3>          
        <div data-holder-for="product"></div>        
    
        <p> </p>       
        <input type="button" value="Save" id="saveSmallForm">
    </div>
    
    <!-- Library of dynamic blocks. Here is only one block named "product" -->
    <div style="display:none">
        <div data-name="product" data-label="Product" class="product">
            <input name="title" style="width:600px"> 
            Price: <input name="price" style="width:100px; text-align:right"> 
            X <input name="amount" class="short" value="1"  style="width:50px">
        </div>
    </div>    
    


    Выкину все лишнее, чтобы стало нагляднее:

    <!--Базовая форма -->
    <div>       
        … Фиксированные поля формы…
        <div data-holder-for="<ПУНКТ_СЧЕТА>"></div>        
        <input type="button" value="Save">
    </div>
    
    <!--Код для динамической вставки-->
    <div data-name="ПУНКТ_СЧЕТА">	
        … Поля отдельного пункта счета …
    </div>
    


    Т.е. у нас есть некая базовая форма с фиксированным набором полей. Внутри этой формы мы размещаем холдер, DIV помеченный атрибутом «data-holder-for». Значение атрибута задает имя блока, который сюда можно вставлять. HTML код этого блока задается отдельно от формы и помечается атрибутом data-name.

    Кнопки добавление, удаления пунктов, поведение drag-and-drop для сортировки и переноса полностью формируется плагином.

    API


    Чтобы получить значения полей формы надо сделать следующий вызов

    var json = $(<ФОРМА>).jqDynaForm('get');
    


    и он вернет JSON объект следующей структуры:

    {
        "number": "123",
        "payer": "Fake Incorporated",
        "productArray": [
            {
                "title": "HP Pavilion g7-2010nr 17.3-Inch",
                "price": "499.99",
                "amount": "3"
            },
            {
                "title": "Samsung Galaxy Tab 2 (7-Inch, Wi-Fi)",
                "price": "248.00",
                "amount": "1"
            },
            {
                "title": "HP Envy 4-1030us 14-Inch Ultrabook",
                "price": "779.99",
                "amount": "1"
            }
        ]
    }
    


    Обратите внимание, что повторяющиеся вложенные блоки автоматически размещаются в массивах. Значения ключей в полях, это атрибут name в тегах INPUT и SELECT.

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

     $(<ПУСТАЯ_БАЗОВАЯ_ФОРМА>).jqDynaForm('set', json);
    


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

    Пример посложнее


    На самом деле, структура формы может быть гораздо сложней, потому что вы можете размещать холдеры не только в базовой форме, но и внутри динамических блоков. Холдер даже может ссылаться на самого себя, формирую древовидную рекурсивную структуру. Вот пример более сложной формы:



    Фичи


    Перечислю наиболее важные фичи:

    • Произвольный HTML код формы без особых ограничений
    • Строгое задание структуры иерархии вложения блоков
    • Создание вложенности в несколько уровней
    • Создание рекурсивных структур
    • Действия над структурой при заполнении формы: Добавление блока, удаление блока, сортировка и перенос (drag-and-drop) блока в другое разрешенное место.
    • Превращение формы любой структуры в JSON объект
    • Воссоздание формы из JSON объекта
    • Создание формы без необходимости программирования. Настройка задается специальными атрибутами


    Также планирую добавить следующее возможности:

    • Упрощенный механизм валидации полей по регулярным выражениям
    • Внешние обработчики событий


    Работа с JSON


    Как работать с полученной JSON структурой. Можно обрабатывать на JavaScript-е, можно отправить на сервер и превратить, например в php-array древовидную структуру.

    Обычно значения полей хранят в базах данных. С классическими «плоскими» формами все понятно. Такая форма замечательно ложиться в одну запись таблицы реляционной базы данных. А тут форма древовидная, и структура данных соответственно тоже древовидная. Вариантов хранения много, вот некоторые:

    • Использование MongoDB (Самый красивый вариант)
    • Хранения JSON объекта в виде текста в обычной реляционной БД. Но это не всегда приемлемо, если необходимо выполнять запросы к отдельным поля формы. Поэтому рассмотрим следующий, компромиссный вариант
    • Хранения полей верхнего уровня в полях реляционной БД, а всех полей вложенных блоков в виде текстового представления JSON объектов в дополнительных полях той же записи реляционной БД.
    • Самый занудный вариант, это завести для каждого вида блока полей отдельную таблицу в реляционной БД и заполнять эти таблицы «вручную», оббегая JSON объект.


    Что меня побудило к созданию этого плагина


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

    Вот типичный ход работы над проекта в стиле «ночной кошмар». От Заказчика с некоторым периодом поступают следующие требования:

    • Сделайте форму для хранения данных о компании. Там надо только поля «название», «город», «улица», «дом».
    • Ой! Оказывается у компании может быть много филиалов. Сделайте, чтобы можно было в форме задать адрес каждого филиала по отдельности.
    • Мы тут подумали, нам бы еще надо, чтобы у каждой компании в форме можно было записать несколько проектов, которые она выполняла.
    • Добавьте, пожалуйста, контактное лицо к каждому филиалу. А… Добавили? А как к каждому телефону указать имя человека? … Нам надо чтобы можно было задать много контактных лиц в каждом филиале и каждому указать отдельный телефон.


    Дабы избежать кошмара постоянного программирования всех этих кнопочек – «добавить», «удалить», «перенести», не мучаться с формированием динамических структур, удобно обрабатывать значения полей, я и задумался на созданием этого плагина.

    Исходники


    Заранее предупреждаю, что плагин еще сырой. Но если у общества будет интерес, то я планирую его дорабатывать и выкладывать следующие версии.

    Страничка проекта находится на Google Code

    Скачать рабочий пример и исходники можно здесь

    А здесь, можно поиграть с живым примером

    P.S.: Буду рад узнать ваше мнение.
    Поделиться публикацией

    Похожие публикации

    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 29

      +1
      Мысли материальны! Утром только подумал, что надо подобное сделать :)
      Хотел использовать это zforms.ru/docs/repeatable-model/
      Но заинтересовал и Ваш вариант
      • НЛО прилетело и опубликовало эту надпись здесь
          –1
          если вы их (данные с форм) будете сохранять, тогда в чем сложность-то?
            +1
            Так же как и любые другие формы.
            Небольшая дополнительная работа может возникнуть только из-за того, что нужно будет проверить данные из полей находящихся во вложенных блоках.
              0
              В рельсах это не проблема благодаря nested_attributes. Кстати в тех же рельсах решал подобные задачи при помощи гема nested_form_for, который генерировал шаблон для формы, а так же умеет делать хелперы для ссылок или кнопок типа «добавить запись», которые собственно и генерируют новую подформу.
          0
          Наверное мне не так часто попадаются странные заказчики, как Вам. Я не вижу очевидного преимущества плагина перед самописом где нужно.
          А вот прекрасную связку label — поле ввода по id Ваш плагин убивает напрочь.
            0
            Многие приложения содержат большое количество форм. Создание обработчиков форм часто отнимают много времени.

            Не отнимают, если использовать несколько иные механизмы по работе с формами.
              0
              Что за механизмы вы имеете ввиду?
                0
                Например, если использовать не селективную модель, как в jQuery, а событийную, и переложить обработчики событий на ноды, то формирование любых поведенческих моделей, как то выборка данных в json-структуры, формирование динамических строк любой формы и содержания, и прочие «трудности» становится простым занятием.
                  0
                  Не кинетесь ссылочками на примеры или где почитать? Заинтересовало.
              +3
              Почему было принято решение писать отдельный плагин, а не воспользоваться knockout js, добавив нужные байндинги например?
                0
                Хотелось сделать работу с формами максимально лаконично, т.е. минимально дополнительных аттрибутов и JS кода. KnockoutJS все же требует больше кода и меньше понятен верстальщику. (Я не эксперт по KnockoutJS, поэтому возможно меня поправят)

                Ну и второе, исторически, код этого плагина сначала писался для внутреннего проекта, который использовал только jQuery. Сначала этот код вовсе не был универсальным. Потом выяснилось что код можно «допилить» до законченного универсального плагина.

                Думаю вы правы, тоже самое можно было бы сделать на базе KnockoutJS. Т.е. получилась бы обертка над KnockoutJS.
                +3
                Напоминает KnockoutJS, где это одна из его возможностей.
                  +1
                  Дабы избежать кошмара постоянного программирования всех этих кнопочек – «добавить», «удалить»

                  Огромное. Человеческое. Спасибо.
                    0
                    А если задействовать html5 forms repeating model?

                    Можно использовать webshim (webforms2), который реализует это дело в любом браузере, пока производители не напишут поддержку repeating model из коробки.
                      +3
                      Почему бы не назначать инпутам имена, такие как «product[productArray][0][title]» — тогда данные можно было бы собирать на сервере без js
                        0
                        Так работают формы в Drupal, кстати.
                          +2
                          Аналогичная мысль возникла. Постоянно пользуюсь такими именами для массивов данных — очень удобно на стороне сервера получать сразу массив. Отлично работает в РНР.
                          Знающие, удовлетворите любопытство — приходят ли данные в виде массивов и в другие веб-ориентированные языки?
                            0
                            да
                              0
                              Нет. В ASP.NET MVC необходимо (для коллекций в первую очередь) формировать запрос по-другому
                              –1
                              Да. Если хочится сабмитить форму без JS то такие имена очень кстати, если вы используете PHP.
                              Только лучше писать не
                              product[productArray][0][title]
                              

                              а
                              product[productArray][][title]
                              

                              Тогда массив будет автонумероваться.

                              Возможно мой плагин и в текущей версии переварит такие имена, хотя надо проверить.
                                +1
                                цифра нужна, чтобы понять, где заканчивается одна запись и начинается вторая

                                product[productArray][0][title] product[productArray][0][price] product[productArray][1][title] product[productArray][1][price]

                                В ruby on rails тоже есть парсер таких конструкций, возвращает хэш. который влегкую скармливается в нестед ресурсы
                              0
                              Делал такую штуку для xml, от рекурсии голова чуть не взорвалась.
                                0
                                Всё это прекрасно, если не считать, что кроме рисования самой формы вдобавок нужна ещё и довольно кропотливая валидация. А без валидации нафиг оно?
                                  0
                                  Я бы так не сказал. Как правило, по сравнению со всем остальным — валидация это просто.

                                  В большинстве случаев для валидации достаточно с каждым полем сопоставить регулярное выражение. Собственно я хочу добавить обработку таких атрибутов в следующей версии. Типа того.
                                  <input name="login" data-regexp="/^[a-z0-9]{6-9}$/" data-regexp-message="Incorrect login" />
                                  

                                  Ну даже если если проверки по регуляркам не достаточно, то все равно проверка сводится к проверке каких либо условий для каждого поля. Обычно это много времени не занимает, и не важно на клиенте или не сервере это делается.
                                    0
                                    В основном да, не важно.

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

                                    Кроме того, валидация на сервере в любом случае же будет производиться.
                                      +1
                                      Имелось ввиду server-side. Без этого, сами понимаете, никуда. Либо вы закрываете глаза на дыру размером с окно.
                                        0
                                        Сервер-сайд валидация зачастую пишется проще чем клиентская. На сервере больше возможностей.
                                        .
                                        • НЛО прилетело и опубликовало эту надпись здесь

                                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                    Самое читаемое