Что нужно от форм?


    В жизни каждого разработчика наступает такой момент, когда ему нужно сделать форму. Вроде бы чего тут сложного — бери, бросай. А нет, форма то, она как живая. У неё есть своё настроение, свои привычки. Выбрал пол — “Ж”, она преобразилась, стала чуть другой, любопытной, спрашивает замужем ли, любимые духи, обувь с каблуком, или без? Но ты же мужик! И тут выбираешь пол “М”, и, как бы, вопросы должны быть другие — холост, любимый сорт пива, любимый спорт. Конечно, можно понаделать кучу формочек для каждого чиха, если “М”, то одна, если “Ж”, то другая. Но такой метод обернется катастрофой на этапе поддержки, да и вообще не в духе красивого кода. Поэтому форма должна быть умной. Очень умной. Она должна знать кто её трогает, чего он хочет, его потаённые желания. Например: есть форма ввода адреса в пять полей
    • страна
    • область
    • город
    • улица
    • метро

    Выбираю я город, и почему бы форме не додумать область и страну? Или выбрал Владимирскую область, зачем мне в списке городов “Москва”? Поле “метро” для Владимирской области тоже не актуально, а когда выбран город Струнино, так вообще издевательство. Занимаясь разработкой клиентской части в корпоративных WEB приложениях вот уже 5 лет я таки познал, как сделать форму умной. Надеюсь, мой опыт будет полезен и вам.

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

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

    В первом случае все выходит довольно печально. Возьмем для примера ветвление по полу. Чтоб не донимать пользователя лишними полями сначала узнаем пол, а потом выводим актуальную для этого выбора форму. Выходит как-то так:


    Строго говоря, такой способ сбора информации называется “мастер”. Он хорош для определенных целей (например: после выбора меняется задача пользователя, ну или данных в принципе очень много для одной формы, или формы отражают разные бизнес объекты), но использовать его для придания динамики — тупиковый вариант.

    Как правило, есть поля, логика которых идентична для всех веток. Самое простое — дублировать эту логику, но это же и самое неправильное. Очень быстро разработчик утонет в собственном коде, а о конфигурировании через настройки и говорить не приходится, они тоже дублируются. А ещё ветвление имеет тенденцию нарастать, а дерево — становиться сложным графом.

    Эффективнее иметь одну форму на один бизнес объект (сущность), изменяющую свое представление по различным событиям. Тут на выручку приходит второй способ — универсальные, динамически изменяемые формы (хотя деление условно, мастер может строиться на таких формах). Реализовать его несколько сложнее, чем ветвление. Раньше я думал “что тут сложного”, но в процессе решения этой задачи количество граблей, подводных камней и тонких материй имело тенденцию плодиться неимоверно! Пройдя все это, я просто не могу не поделиться своей болью и опытом.

    Элементы динамики


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

    1. Видимость
    2. Обязательность
    3. Доступность


    Если с доступностью и обязательностью все более менее понятно, то вот видимость — вещь далеко не тривиальная, и управление ею тесно связано со способом размещения полей (пошли подводные камни).

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

    Фиксированные формы


    С полями, фиксированными в абсолютных координатах (absolut layout есть везде), работать в плане размещения проще — сделал сетку для выравнивания и мышкой расставляй поля как душе угодно, просто, непринужденно. Бывает что поле должно быть не больше чем 40em, ну хоть убейся, а с фиксированными размерами — нет проблем, сорок, так сорок. Проблемы начинаются когда начинается управление видимостью. Вот есть три поля, прячется центральное, остается дырка:

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

    Если форма с фиксированными размерами и координатами, то динамическое управление видимостью становится самой сложной задачей. Это надо сразу же четко представлять и выбирать, то ли удобство и простота наброса, то ли динамическая видимость со всеми плюшками.

    Резиновые формы


    А что же будет с резиновыми формами, которые не фиксированы в абсолютных координатах? Будет все прекрасно! Дырка просто схлопнется:


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

    Эта проблема усугубляется в момент, когда только полей на форме мало. Хочется иметь красивую, фигуристую форму. С парой колонок полей, с панелью закладок, где будут табличные части, с группировками и прочим добром.

    На форме все что не поля — это контейнеры (мы пока будем рассматривать такую концепцию). А контейнеры содержат поля. А поля могут стать невидимыми, и контейнеры в этом случае окажутся пустыми. Пустой контейнер хуже дырки на форме. Он должен уйти в подполье вслед за своими полями. Но вот незадача, контейнеры могут быть вложенные, много раз вложенные и очень много раз вложенные (другие мне не попадаются). Возникает необходимость обхода дерева контейнеров на предмет наличия видимых полей в них. Обход, к счастью, вниз. В любом случае форма будет стремиться к иерархии, вроде такой:



    Кажется алгоритм простой — изменили состояние видимости поля, обошел контейнер её содержащий вниз, до первого видимого поля. Нашли такое поле — баста, контейнер видим, не нашел — контейнер прячется, и соответственно, повторяем для родительского контейнера ту же процедуру. Это одно из решений.
    Иногда ещё и хочется спрятать не отдельные поля, а целиком весь контейнер. Появляется признак некой абсолютной спрятанности контейнера, чтоб он не вылез даже если поле в нем “видимо”. Довольно быстро этот механизм нарастает и усложняется, лучше сразу задуматься, где все будет считаться, на сервере или на клиенте, или смежно. Разделение ответственности в этом месте очень важно.

    А от куда берется динамика?


    Она берется с сервера (К.О.). По сути это набор каких-то правил, которые (сильно желательно) задаются пользователем через интерфейс. Простейшим кирпичиком динамики является ПУЗ (Поле-Условие-Значение). Упрощенно это выражение вида: “Если значение поля равно значению, то поле видимо, иначе не видимо”. Условие сравнивает некое значение из какого-то поля на форме с эталоном, и, в зависимости от выбранного условия, поле, к которому относится этот ПУЗ, изменяет свое состояние (видимость, доступность, обязательность). Например в этой форме есть ПУЗ скрытия поля «Метро», если в городе его нет. Вот на таких элементарных условиях строится динамика. Конечно она может быть не только на ПУЗ-ах. Это могут быть и статусы документов, и доступные поля в операции, и много страшных слов. Но на уровне клиента — это все абстракция и не суть откуда приходит, важно как это отрабатывать.

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

    Резюме...


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

    Немного позже (если будет интерес) я расскажу подробнее о источниках динамики, а именно — событиях полей, динамических фильтрах, глубже скажу про ПУЗ-ы, и коснусь статусов и операций. Все это общие темы для учетных систем, и не только.
    Share post
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 74

      +6
      В целом неплохо, но эта тема может быть довольно специфичной.

      На моей практике использовалось два варианта динамических форм совершенно не похожих друг на друга:

      1. Форма только со справочными значениями изменяющаяся каскадом. Для каждого элемента формы (select) присутствовала следующая информация: Описание основных свойств, ссылка на список возможных значений (обычно таблица, или функция БД) и список полей зависящих от него. HTML код у всех элементов был одинаков. Таким образом обновив одно значение процесс обновления далее каскадом шел вниз по дереву.

      2. Более интересный вариант. Форма содержала огромное количество элементов. Количество и типы элементов заранее неизвестны. Для всех элементов присутствовала мета информация — тип поля (текстовое, выпадающий список, дата), доступность для редактирования, ссылка на родительский элемент от которого зависит поле, и т.п. Таким образом получилась форма с полями разного типа, и что более интересно с полями которые могут меняться в зависимости от изменения других полей. Вся мета-информация хранится в БД.

      Учитывая вышесказанное, могу сказать только одно — универсального решения здесь нет, т.к. это чистой воды бизнес-логика.

      Кстати на счет ПУЗов — раньше не слышал про такое сокращение. Это какой то общепринятый термин, или лично ваша терминология?
        +1
        Это терминология, которая устоялась у нас в компании. У нас эти самые ПУЗы используются еще много где, кроме форм :)
          +1
          Кстати, у меня при просмотре примера выполняется 131 запрос к серверу из них 81 запрос подгружает js. Это специально так задумано?
            +2
            Это не паблик версия, там не оптимизирована упаковка скриптов :) По секрету, мы сейчас тестируем нагрузку ;)
              +1
              Для получения максимального хабраэффекта нужно было писать в обед ИМХО :)
            +3
            Наследие нынешней команды :) Вообще термин распространенный, я его слышал ещё во время учебы, но прицепился он только теперь.

            То что вы написали по статье очень интересно, мне ближе всего 2-ой вариант.

            В принципе обычно так и делается, ибо гибко. По сути поле формы — это отражение поля БД. Вот отделение метаданных поля формы от метаданных поля БД очень важный момент. По мере накопления требований я пришел к выводу, что нужна многослойная структура, где каждый слой накладывается на последующий. Я надеюсь подробнее написать об этом в следующих статьях, хочу лишь сказать, что из накопленных требований складывается оптимальная цепочка ПолеБД-НастройкаПоля-НастройкаПоляФормы, при этом за отображение отвечает специальный компонент (скажем поле Int может быть как числом, так и цветом, или вообще токеном). На эти сущности накладываются прочие вещи, как-то доступность полей на операциях, статусах и т.п. В общем тут нетривиально, и дико захватывающе %) Охохо…

            *Поле БД — это я загнул конечно, мы оперируем Сущностями и их полями.
              0
              Что то чем дальше, тем непонятнее.
              Про какие слои речь? Сбор свойств элемента формы?
              Мне кажется не стоит все так усложнять. Таблица со значениями полей отдельно, таблица с мета-информацией отдельно, связь по ключу. Все, дальше дело техники :)
              Ну в принципе можно раскидать по отдельным табличкам но это уже зависит от конкретной задачи.
                +3
                Ну да, сбор свойств :) Чем больше требований, тем больше сложностей. Бизнес-логика порой требует таких хитроманских штук, что ой-йой. К слову у нас формы ещё наследуются друг от друга.

                Фишка в том, что многие формы имеют много общих свойств. Например форма Физических и Юридических лиц — очень похожи. У них одинаково отрабатывают всякие банковские реквизиты, номера счетов и прочее. Различаются реквизиты самого лица, на которых тоже есть логика. Так вот, на уровне БД, скажем номер счета, это ссылочное поле, оно обязательно и для Юр лица и для Физ лица. А вот скажем поле «Банк» обязательно только для Юр лица и по какому-то условию (он является депонентом). Значит мы делаем поле «Счет» обязательным в принципе (не имеет смысла Юр и Физ лицо без счета), а поле «Банк» настраиваем для Юр лица, но не в сущности (т.к. Юр лицо может быть и без банка), а на уровне настроек поля, это логика прикладного уровня, ну и там вешаем нужные обработчики событий. А теперь, на уровне настроек формы — выставляем способ отображения этого поля как «поле банков» (условно, очень условно), и помещаем его в нужное место на форме. После этого идет наложение всяких состояний. Скажем Юр и Физ лица — это документы, а документ нельзя так просто редактировать, для этого есть бизнесс процесс. Так вот, на момент заведения документа можно редактировать и счет и банк, а вот потом счет изменять нельзя, он у лица на всю жизнь (такой у нас абстраткный бизнесс процесс в вакууме). И на уровне статуса документа (на статусе Создание) мы накладываем слой настроек — поле доступно на редактирование, а на статусе «Изменение», «Аннулирован» поле недоступно. Ну как-то так, если на пальцах и абстрактно. Надеюсь хоть что-то прояснилось (кроме того, что все это печально и запутанно).

                Я просто боюсь развернуто отвечать, т.к. это ещё больше запутает и вы подумаете что я обкурился :) Мои методы не претендуют на эталон, но позволяют реализовывать много фишек. Я надеюсь рассказать о них в других статьях подробно.
                  +1
                  Теперь как раз все понятно.
                  Есть некоторые стереотипы для форм, которые включают в себя несколько атрибутов (например на этой форме все поля должны быть красного цвета, а уголки закругленные), я так понял что вы для каждой такой группы заводите таблицу?
                  Если так, что два последних комментария можно заменить словом нормализация :)
                    0
                    О да, она великая :) Иногда туго со словами, но все сводится к нормализации и разделении ответственности, в точку!
            +3
            В некоторых случаях disable на элементе будет комфортнее, чем его исчезновение.
              0
              Это так, но далеко не всегда. Иногда по условию надо скрывать целые группы из 5-10 полей. В таком случа гораздо удачнее их скрытие, т.к. они перестают загромождать форму (на которой может быть еще штук 50 полей ;)
                +1
                Кстати к вопросу о метро — было бы логичнее по умолчанию это поле не показывать, а показывать только при выборе города где метро есть.
                Ясное дело что это демка, но вызвало некоторый диссонанс. Ведь если я не выбрал город, то в списке должно быть несколько сотен станций, а если я выбрал город, то искать нужную станцию будет гораздо комфортнее.
                  +1
                  Я думал над этим, но решил, что это чертовски хороший способ показать автовыбор города по выбранному метро ;)
                    0
                    Ну это с какой стороны посмотреть. С учетом того что есть автокомплит на этом поле, способ конечно хороший, но если бы я жил в Певеке, где нет метро, мне бы такая форма могла сразу субъективно вызвать отторжение.
                    Да и лишний трафик в конце концов.

                    ИМХО лучше делать форму проще, особенно если полей и без того хватает.
                +2
                Все это тонкий балланс между удобством восприятия и доступностью информации. Лично я за disable в малых дозах. Но если часто дергается видимость — это зло и адЪ. Слава богу решают такие вещи аналитики и прикладные разработчики. Главное дать им в руки пистолет, чтоб они могли прострелить себе что угодно, хоть ногу! Ну или убить всех зайцев в радиусе 10 километров.
                  +1
                  Главное дать им в руки пистолет лук, чтоб они могли прострелить себе что угодно, хоть ногу колено!
                    0
                    Забыл еще зайцев на драконов переделать, ну да и ладно.

                    А что будет, если к луку приставят «не того человека»? Он же может все испортить… С таким подходом ведь и до «rm -rf /» недалеко…
                      +1
                      По этому есть функциональные и организационные права. А ещё роли и пользователи ;) Роль стрелка «не тому человеку» не положена.
                        0
                        Ну вот:) Теперь все разложилось по полочкам. Теперь буду ждать статью по этому реквесту…
                    +2
                    Мой опыт подсказывает, что если дать аналитикам в руки пистолет, то надо быть уверенным что находишься в танке. :)
                  +1
                  Как все сложно, оказывается…
                  Но тут, IMHO, изложена позиция дизайнера интерфейсов (который это все должен придумать), а не непосредственно разработчика (который воплощает задумку). Как разработчику скаешь, так он и сделает (если конечно разработчик не доморощенный хацкер-шестикласник). Так должно быть, мне кажется.
                    +3
                    Как показывает практика — надо дать возможность любому настроить визуальную часть сколь угодно сложно. И желательно отделить часть программиста, от части настройщика (это, как правило аналитики, но если все сделано мега круто, то вообще пользователи). В общем рабочий процесс сводится обычно к кодированию логики в отрыве от визуализации, а остальное доводится уже бизнесом и аналитиками. Задача программиста — обеспечить консистентность экземпляра сущности, задача настройщика — обеспечить удобство наполнения экземпляра сущности. Вот и получается что нужны умные формы.
                      +3
                      После вашего комментария я наконец понял-таки, о чем была статья.
                      Да, настраиваемые формы это круто!
                        +2
                        Эх, я надеюсь что в будущем более понятно смогу написать. Может когда будет речь о деталях, то станет очевиднее что к чему, тут как бы с большой высоты обзор :\
                        +1
                        Читая этот комментарий вспомнилась картинка проекта «Карусель», а именно пункт «чего хотел пользователь».

                        Не принимайте на свой счет, я лишь хотел сказать что часто бывают ситуации, когда мега-крутой функционал пользователи ценят меньше чем внешнюю простоту.
                          0
                          Увы, я это знаю. Зачастую многие фишки умирают из-за того, что они слишком сложны. Вообще программистам нельзя давать в руки власть :) Я стараюсь придерживаться правила Apple — если 9 вещей из 10-и можно сделать просто, а 10 из 10 только сложно, то мы делаем лишь 9 вещей (но по человечески). Как-то так.
                      0
                      В формах ввода вбивают данные на автомате и как можно быстрее. В них НЕЛЬЗЯ использовать ветвление или изменение полей по контексту. Автоматизм пропадёт.
                        0
                        Смотря какие формы ввода. Вот я знаю в одном СЭД-е форму ввода на 250 полей. Тут речи о скорости в принципе нет. А мастера иногда удобнее и быстрее. Вообще все очень по разному, но да, главное скорость работы оператора. Тут важно даже не ветвление, а грамотные хоткеи и быстрые переходы.
                          +1
                          Ветвления удобны в случае, если один человек по логике должен заполнить форму один раз (к примеру- на получение загранпаспорта). Если человек должен работать с формой постоянно, то удобство от ветвлений немного ниже.
                          +4
                          Было бы интересно почитать про структуру БД, потому что сам недавно столкнулся с похожей задачей — разрабатывали мастер из 3 шагов для добавления объектов коммерческой недвижимости. 42 типа объектов (склады, офисы, магазины и т.д.) с собственным набором характеристик для каждого типа объекта, плюс большое количество зависимостей (если объект на 1 этаже — спросить есть ли отдельный вход, если есть вход — спросить есть ли возможность повесить вывеску над входом и все такое).

                          У нас получилось 3 варианта ПУЗов — когда поле равно значению, когда поле не равно (например, если объект в черте города, и для города есть список районов то показать выбор районов, если списка нет то спросить насколько далеко от центра, если вне города то спросить как далеко до города), и третий вид — больше чем значение (для зданий больше 1 этажа спрашивать есть ли лифты/эскалаторы)

                          Отдельно храним список полей формы, отдельно — шаблоны (текст, селект, чекбоксы, чекбоксы и «другое») и т.д., Соответсвенно есть таблицы с пузами, есть еще куча всего.

                          Отдельная история как хранятся значения множественных полей (например чекбоксов) и как хранятся множественные поля со множественных значениями (например для объекта можно указать до 5 ближайших станций метро с расстоянием до каждой из станций).

                          Вроде бы обычный Eav, но не совсем. А ведь все это еще надо подготовить для удобного поиска объектов по этим параметрам.

                          В общем пишите дальше, очень интересно, в том числе и сравнить ваше решение с нашим :) потому что задача в целом простая, но очень много тонкостей.

                          На один js который бы позволял удобно работать с каскадами зависимостей полей и корректно обновлять наборы полей на странице в зависимости от выбора пользователя, при изменении старого поля, от которого уже 2-3 уровня зависимостей вылезло, мы немало времени убили.

                            +1
                            Бамп :) Тоже интересует этот вопрос, можно ожидать статью на эту тему?
                              +1
                              Ну вроде как автор обещал продолжить раскрывать вопрос, если мне будет что добавить или наши методы будут сильно отличаться — напишу свою
                                +1
                                От моих братьев по оружию — обязательно :)
                                  +1
                                  Наверное забабахаю статью, как закалялась стальделались ПУЗы :) Заодно, опишу где и как мы их применяем. Тизер: используем одну и туже структуру для построения Expression'ов при выборках LINQ и для скрытия/показа полей на форме и не только :)
                                    0
                                    Предчувствую неделю умных форм на хабре :)

                                    Я бы тоже написал, да только времени нет.
                                +1
                                >У нас получилось 3 варианта ПУЗов
                                У нас есть варианты на все случаи жизни, вплоть до «в списке», «не в списке», «содержит» и так далее. Там очень много нюансов и сложностей.
                                >Отдельная история как хранятся значения множественных полей (например чекбоксов)
                                Так это же типичное Many-to-Many, так и хранится у нас :) Просто отображается потом хоть чекбоксами, хоть таблицей.
                                Кстати, EAV у нас нет совсем. По крайней мере, мы пока обходимся без него.
                                  +2
                                  У нас задачи «создать вариант на все случаи жизни» не стояло, был конкретный случай и под него хватило 3 пузов. Нюансов там действительно масса.

                                  По поводу many-to-many, в случае с чекбоксами это действительно выход, но что если поле подразумевает ввод двух значений? Например чекбоксы " наличие в здании:
                                  Лифтов грузовых
                                  Лифтов пассажирских
                                  Эскалаторов
                                  Траволаторов" и если человек ставит галку его сразу же просят ввести количество лифтов, то есть как бы в рамках одного поля человек указывает несколько значений, при этом поле может быть множественным (то есть человек может выбрать 3 значения, внутри которых указать еще по несколько, например объект находится недалеко от 2 станций метро, при этом до первой 2 минуты пешком, а до второй 5 минут на автобусе).

                                  Тут уже классическое многое-ко-многим не катит, поэтому мы используем хитрый Eav.
                                    0
                                    Было бы интересно почитать про ваш Eav.
                                    По поводу лифтов и т.п., а вариант с двумя связанными полями не катит?
                                    Можно же такие поля обрабатывать каскадом например.
                                      0
                                      В нашем случае это решилось бы невидимыми по дефолту полями, которые показались бы при выборе чекбокса с лифтами. Не очень красиво внутри, зато универсально.
                                      Ну, а вообще, галку и поле с количеством можно заменить только самим полем с количеством. Пустое поле и будет означать отсутствие лифтов и т.п.
                                        0
                                        Невидимые по дефолту поля у вас можно сделать появляющиеся в одной строке с выбранным чекбоксом?

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

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

                                        Если бы это была просто форма заполнения объекта то не было никаких проблем. Грешников делом, мы даже сначала думали захардкодить 42 формы и не париться, но на первой же форме поняли что это плохая идея и решили заморочиться и сделать универсальное решение.

                                        Зато теперь кайф, сидим новым типам объектов просто поля раскидываем и не парится :)
                                          +2
                                          > Невидимые по дефолту поля у вас можно сделать появляющиеся в одной строке с выбранным чекбоксом?

                                          Да, можно. В примерчике поле «Метро» может быть изначально невидимым, как после выбора города «Певек». Там контейнер колонок, в нем две колонки, в каждой из них поле. Одно поле прячется — второе раздвигается на всю ширину. Вообще это можно по разному делать, если ситуация типовая — я бы ввел структуры, и обрабатывал тип поля структуры как одно сложное поле, внутрях которой своя сложная логика (мы к этому подходим).

                                          >Грешников делом, мы даже сначала думали захардкодить 42 формы и не париться, но на первой же форме поняли что это плохая идея

                                          Вам чертовски повезло ;) Очень жирная грабля однако!
                                            +2
                                            собственно, мы так и сделали — у нас есть сложный тип поля «метро», есть тип «лифты» и еще парочка специфических типов, подраззумевающих сложную структуру данных внутри. Тут просто возникает вопрос — как хранить введенные пользователем данные? С учетом что там не просто многое-ко-многим, а многое-ко-многим плюс кучка параметров к каждой связи.

                                            Тут то к нам и пришел ЕАV, с доработкой — мы храним не просто entity — attribute — value а entity — attribute — value index in attribute — subvalue index in value — subvalue.

                                            Соответсвенно для полей, подразумевающих единственное значение, два этих индекса лежат нулевые, для полей подразумевающих несколько значений — меняется индекс значения внутри поля, для полей, подразумевающих несколько значений внутри нескольких значений — есть еще один индекс )

                                            Да, такая база нифига не нормализована, зато работает достаточно быстро и понятно, плюс довольно удобно работает поиск по объектам (правда, через сфинкс, который предварительно все индексирует).

                                            В целях нормализации можно было наплодить еще пару таблиц для хранения значений сложных полей, но мы подумали что наш способ это как раз тот случай когда намеренная денормализация идет во благо
                                    +2
                                    К слову можно обойтись и без EAV в вашем случае (но нужно ли). Все же EAV вещь такая, суровая сама по себе :) Главное чтоб о ней не знал уровень View, где живет форма — ИМХО.

                                    Обязательно напишу подробнее о структуре, но к слову у нас ORM, и как бы обычные таблицы-сущности. Там мультилинки, ссылки, ТЧ, прочая фигня, хехе :) Но это не уровень абстрации View, повторяюсь.

                                    А по поводу js, понимаю вашу боль… По этому мы пришли к выводу, что все это надо считать на сервере, ибо удобнее управляться на C# с этим, чем на js. А клиент должен только четко выполнить команды сервера по выставлению состояний полей и контейнеров (у нас это зовется состоянием формы).
                                      +1
                                      Форма у нас изначально генерится средствами xslt на основе двух XML-один с данными о полях, другой с данными объекта Соответсвенно.

                                      Для каждого типа полей (текст, чекбоксы и так далее) есть два xsl-шаблона, один для редактирования информации, второй для отображения.

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

                                      О еав знает только та часть модели, которая отвечает за сохранение информации в бд, ну естественно та, которая ее оттуда получает.

                                      Весь остальной код оперирует уже нормальными объектами (я говорю сейчас не про объект формы, а про тот объект, характеристики которого мы этой формой заполняем)
                                        0
                                        Интересно у вас :) К нам приходит JS код, который уже конструирует ExtJS объекты, на которые накладывается состояние формы, и по мере ввода изменяется. Так достаточно гибко получается. Скоро будет приходить вообще только JSON структуры, и JSON состояния и данных. Слава всем богам, чистым HTML я не оперирую :)
                                          0
                                          У нас как такого глобального состояния формы нет. Точнее оно есть в неявном статичном виде — в виде значений полей, которые были заполнены на предыдущих шагах. Соответсвенно оно не меняется в процессе заполнения.

                                          На текущем шаге имеется только состояние каждого отдельно взятого поля, причем есть поля «корневые», которые зависят от предыдущих шагов и их свойства не могут меняться в процессе заполнения, и зависимые — которые появляются и исчезают и меняются в зависимости от констант, пришедших с прошлых шагов, и данных, введенных пользователемм на текущем шаге.

                                          При этом в json приходит информация о состоянии полей, которые надо поменять (удалить, добавить и все такое), в том числе приходит и HTML новых полей.

                                          Причем для ускорения работы обработка некоторых пузовв у нас не требует обращения на сервер и происходит прямо на клиенте, на основе состояния поля и его свойств.

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

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

                                          Зато с учетом того, что нам не требовалось универсальное решение на все времена и все задачи, пойдя на некоторые компромиссы мы добились приемлемого времени разработки и достаточного уровня универсальности (42 типа объектов по 30-40 разных полей в каждом, поля ветвисто зависят друг от друга)
                                        +1
                                        Про js — зря вы так. Тут главное подумать как следует, и результат может быть весьма приятным.
                                          0
                                          Не прочитал про ExtJS и JSON когда писал комментарий. Беру свои слова обратно :)
                                            0
                                            Да нет, все хорошо +)
                                            0
                                            Раньше была реализация динамики чисто на JS. Это был метод на 600 строк (естественно он только логику отрабатывал, пуская и дергая нужные методы и события). В итоге я почувствовал что зарываюсь и мы сделали все на сервере. Работает достаточно стабильно, и как показала практика — проще менять и дополнять логику в сложных местах. Увы, сразу продумать зачастую просто невозможно, нет всего спектра требований. С каждой итерацией они добавляются и добавляются. Хотя я изначально форсил тему клиентского управления, ни что так не завораживает, как всплывающий эвент +)
                                              +1
                                              Бабблинг в js — одна из самых красивых его частей :)
                                        0
                                        видимо, это какой-то «тренд»: вы практически один-в-один пересказали один из механизмов тонкого, т.н. «управляемого» интерфейса 1С8.2. А именно, управление видимостью через условное оформление в управляемых формах. Если кратко, то в последней версии платформы появилась возможность задавать правила: «Если Реквизит1 больше НекоторогоЗначения, то применить к элементу формы такой-то набор оформления». В свою очередь, наборы оформления могут включать помимо параметров цвета или шрифта, ещё и признаки видимости-доступности. При этом изменения шрифта, цвета или доступности выполняются на клиенте, однако изменение видимости уже требует серверного вызова, поскольку пересчёт расположения элементов выполняется только там.
                                          +1
                                          Фишка в том, что в 1с вы пользуетесь уже готовым решением и просто настраиваете формы, а в топике речь о том, как самому сделать такой «конфигуратор форм» со всеми плюшками
                                            –1
                                            фишка в том, что всё уже придумано до нас, и позволяет сосредоточить усилия на реализации бизнес-логики (вроде про ERP-системы речь), а не на том, как правильно сериализовать кусок js-кода. И, заметьте, в статье описывается концепция, но не реализация, про реализацию нафлудили уже в коментах.
                                              +2
                                              А никто не говорил что автор придумал что то совершенно оригинальное, мегауниверсальное подходящее на все случаи жизни решение.

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

                                              Ну и в конце концов это же хабр — единственное место где из комментариев узнаешь больше чем из топика :)
                                                0
                                                Суть в том, что то, что придумано до нас может не подходить конкретно под ваши требования. Боее того, я не знаю систем на рынке, которые позволяют делать то, что умеем делать мы с формами без участия программиста.
                                                +1
                                                Я бы сказал что в случае с автором, 1С совершенно непременима :)
                                                Так же как и в моем случае, и во многих других впрочем.
                                                0
                                                Я за демократию, чем больше систем на рынке — тем лучше ;) Думаю 1С пришли к этому своими путями, и это ещё один сигнал что это правильно. Мы все же разработчики, и кое-кто из нас, возможно, разрабатывает тот самый 1С, и это дико интересно! Статья скорее вводная, в принципе по ПУЗ-у можно выполнить любой код в контексте элемента сущности, формы и самого поля. Но это уже совсем другая история…
                                                +1
                                                И вот приходит юзверь с отключённым js.
                                                  0
                                                  И из IE 6 :)
                                                    0
                                                    Я даже с последней Opera только с третьего раза форму увидел.
                                                    Уж лучше форма без динамики, чем та, которая с 3 раза лишь открывается.
                                                      0
                                                      У нас система имеет вполне конкретные требования к браузеру(а то, что вы увидели — как раз небольшая часть нашей системы) Думаю, это понятно, что при разаботке ERP систем такие требования ставятся или разработчиком или заказчиком, также возможно, у вас не все подгрузилось.
                                                    +1
                                                    По картинкам видно, что уши растут из ExtJS
                                                    Т.ч. заранее предполагается, что будет включен JS, установлен относительно новый броузер, комп и сеть у юзера такие, что смогут заглотить мегабайт JS и при этом не подавиться.
                                                      –1
                                                      Строго говоря — это Ext.Net, библиотека компонентов ASP.NET на ExtJS (версии 3.4, четверка у них в разработке сейчас). А так то да, управляемая среда, корпоративное приложение, широкий канал, браузер IE 7+, FF 3.5+, Chrome 9+, версию опервы не помню :) Вы на 100% правы.
                                                    –2
                                                    Как-то у вас абстрактно все описано. Ведь управление формами в разных платформах/фреймворках сделано по-разному. Потому стоило бы писать, что мы будет рассматривать это в применении к такой-то системе. Плохо, что в статье использован ExtJS. Это крайне тяжелый, некрасивый, уродливый и тормозной фреймворк, в котором, на мой взгляд, есть неудачные решения (напрмер, лейауты на яваскрипте, когда у нас уже есть HTML).

                                                    Также, статья хреновая, так как в ней дается абстрактное введение и ничего конкретного. Зачем? Вы хотите статью на 10 частей разбить что ли? Пишите все сразу.

                                                    Что касается дырок в формах — ну не надо использовать абсолютное позиционирование. Это конечно, было актуально во времена Windows 3.11 и 8 Мб памяти, но точно не сегодня.

                                                    Если у вас задача сделать форму с зависимостями на HTML/JS, взгляните на Zforms (написано человеком из студии Лебедева). Эта система не требует ни монстра ExtJS, ни jQuery, а точно так же позволяет скрывать/дизаблить поля по условию, убирать и добавлять пункты в селекты. Логика зависимостей в ней хранится в виду аттрибутов тегов.
                                                      0
                                                      >Также, статья хреновая, так как в ней дается абстрактное введение и ничего конкретного.
                                                      Если написать все и сразу — просто не влезет в ограничение на объем статьи хабра.
                                                      >Если у вас задача сделать форму с зависимостями на HTML/JS
                                                      У нас вся система сделана на ExtJS, а Ext — это далеко не только формы.
                                                      >Логика зависимостей в ней хранится в виду аттрибутов тегов.
                                                      ОМГ, предлагаете html с сервера слать каждый раз?:) Или всю сложную логику на клиента сразу передавать?:) А ведь она у нас пишется в очень свободной форме на C#
                                                        0
                                                        > А ведь она у нас пишется в очень свободной форме на C#

                                                        Это очень спорное решение, так как требует на каждый чих обмена данными с сервером. Хорошо, давайте почитаем, что вы в следующей статье напишете.
                                                          0
                                                          Спорное решение, согласен, однако у нас уже был опыт написания логики форм на клиенте, и поверьте, то, что есть сейчас — гораздо лучше. Ведь нам важно в первую очередь простота, скорость и мощь инструмента, т.к. лишний обмен с сервером в случае erp системы не так страшен(ведь она работает в 90% в локальной сети)
                                                          +1
                                                          Ну, мы шлем HTML с сервера, правда только код новых появляющиеся полей.

                                                          Логика зависимостей частично, для некоторых простых случаев, у нас тоже лежит в аттрибутах, но мы ее туда добавляем js-ом, с сервера она приходит не в HTML а в json, сделали так чтобы быстрее обрабатывать, мало ли какая скорость интернета у человека.

                                                          Сложную логику на клиента перекладывать нет смысла, а вот в сторону комбинированного подхода — простые пузы обрабатывать на клиенте, сложные на сервере — мне кажется, можно посмотреть.
                                                            +1
                                                            ДУмаю, что мы к этому придем. Сейчас у нас идет обработка пуза на сервере, конвертация его в Expression и расчет по нему уже состояния формы. В принципе, нам ничего не мешает конвертить этот экспрешен в JS и отдавать его на клиента
                                                        +1
                                                        Этот пост надо разместить не в ERP-системах, а в блоге про интерфейсы.
                                                        вот тут habrahabr.ru/blogs/ui/
                                                        это раздел про организацию интерфейсов
                                                          0
                                                          Спасибо за ваше мнение, я ответил в почту почему так, как есть, и что будет если это неправильно. Обещаю что чистота блога ERP не пострадает :)
                                                          +1
                                                          Обожаю ExtJs. :) Спасибо за статью.

                                                          У нас сейчас есть подобный функционал. И реализован он примерно следующим образом.

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

                                                          Связи шагов тоже хранятся. Там зависимости от значений параметров.

                                                          Only users with full accounts can post comments. Log in, please.