Оглавление
Связанные данные и расчет зависимостей
Обработка события ПриИзменении
Отражение состояния в интерфейсе
Управление видимостью и доступностью
Группировка данных и элементов
Модель объекта и программный интерфейс
Проверка заполнения при вводе (история) и при копировании
Я исхожу из того, что хороший интерфейс является одним из ключевых факторов успешности бизнес-приложения. Для его проектирования можно привлечь аналитиков или специалистов по взаимодействию. Однако качественная реализация такого интерфейса - второй фактор успеха, и здесь нам потребуются лучшие решения и разработчики. Поиску лучшей технологии для реализации качественных динамических интерфейсов и посвящено мое исследование, результатом которого стала разработка подсистемы «Управление состоянием».
Современные платформы избавляют разработчиков от необходимости попиксельного программирования элементов форм. Технология управляемого интерфейса избавила нас также от необходимости адаптировать пользовательский интерфейс под различные разрешения экрана и платформы. Единожды разработанная форма может быть переиспользована для десктопных, веб и мобильных приложений. Более того, для того чтобы интерфейс работал, порой не нужно даже ничего программировать - достаточно связать элементы формы с данными.
Бизнес-пользователи ожидают от интерфейса отзывчивости, простоты использования, отсутствия дублирования ввода данных. Бизнес ожидает, что такие интерфейсы будут способствовать лучшей продуктивности работы пользователей, гарантировать консистентность и отсутствие мусора в данных. Также бизнес заинтересован в достижении желаемого результата с минимальными затратами, что может быть достигнуто при использовании технологии. Распространенность шаблонных технологичных решений делает разработку качественных интерфейсов быстрой и приемлемой по затратам задачей. Программисты также в праве ожидать, что технология позволит им меньше писать boilerplate кода, по максимуму переиспользовать уже разработанные алгоритмы, например, для работы с объектом информационной системы: как через пользовательский интерфейс, так и через программный.
Однако любой интерфейс работает с данными. В обычной практике данные связаны между собой. Работа с зависимыми данными позволяет минимизировать ошибки путем уменьшения объема за счет уточняющих реквизитов. Например, перед выбором договора пользователю предлагается выбрать организацию и контрагента. Это связанные данные, здесь действующий договор может быть определен однозначно для выбранной организации и контрагента, ну или список таких договоров может быть сильно ограничен и потому выбор конкретного значения договора не должен составить большого труда для пользователя.
С версии 8.2 в платформе 1С появилась возможность описывать связи параметров выбора. Этот механизм отлично подходит для декларативного задания связей реквизитов элементов формы. Однако существующие ограничения не позволяют в полной мере задействовать встроенный механизм.
Связи можно настраивать только на уровне реквизитов данных. Например, можно указать связь договора с организацией и контрагентом через связь соответствующих реквизитов справочника Договоры: Организация и Владелец. Если, например, у справочника Договоры нет соответствующих реквизитов, а связь образуется через отношение с табличной частью договора "Участники", то такую связь в платформе установить будет уже невозможно. Это ограничение можно обойти при использовании подсистемы Работа с данными выбора.
Настройка поведения платформы при изменении связанных данных тоже не балует возможностями. Здесь платформа предлагает на выбор один из возможных вариантов реакции: очистить или оставить прежнее значение. Первый вариант может заставить пользователя повторить ввод, что не очень хорошо, но не приведет к неверным данным в системе. Во втором варианте может произойти нарушение консистентности данных, что уже просто недопустимо. Понятно, что практическое применение такой настройки достаточно сомнительно. Изменить поведение системы можно с помощью встроенного языка. Давайте вместе найдем способ улучшить поведение системы для связанных данных - это потребуется нам для понимания решения, представленного в статье.
Единственный способ изменить поведение событийно-ориентированной системы - это найти подходящее событие и вставить в его обработчик нужный код на встроенном языке. Для расчета зависимостей подходящим событием является - "ПриИзменении". Так, для нашего примера, нам потребуется добавить код для обработчиков элементов формы Организация и Контрагент. Наш код должен будет делать следующее: проверить, соответствует ли связанный реквизит Договор с текущими значениями Организации и Контрагента. Если значение договора не соответствует условиям связей, то договор необходимо очистить и попробовать определить подходящее значение для новых условий. Если значение однозначно, то пусть система выберет его, иначе пусть возможность выбора останется за пользователем. Чтобы не дублировать код обработчиков события, вынесем его в отдельную процедуру - что-то типа "ПриИзмененииОрганизацииИлиКонтрагента", а в сами обработчики вставим её вызов.
Как видим, в предложенном решении на первый взгляд нет ничего сложного. Протестируем наше решение на предмет масштабирования и поддержки. Если нам потребуется вставить новую зависимость, например, договора от вида операции, тогда будем действовать по той же схеме: добавим обработчик и вставим вызов нашей процедуры. Еще нам нужно будет в процедуре учесть значение новой связи. Однако, что если от вида операции есть и другие зависимости? Тогда нужно будет разработать еще одну процедуру, обсчитывающую зависимые элементы и добавить ее вызов в обработчике. В общем случае получается, что для каждого элемента формы необходимо добавить обработчик события "ПриИзменении". Также требуется добавить процедуры расчета зависимых реквизитов, вызовы которых необходимо вставить в обработчики событий элементов, от реквизитов которых они зависят.
Это вполне рабочее решение, однако есть решение получше. И это не мое решение, я его где-то подсмотрел. Оно заключается в том, что вместо написания множества обработчиков событий элементов формы вся обработка изменений реквизитов реализуется в одной процедуре с рекурсивным вызовом - что-то типа "ПриИзмененииРеквизита(ИмяРеквизита, ИзмененныеРеквизиты)". Это решение лучше предыдущего, ведь здесь не требуется писать отдельные обработчики, достаточно их назначить на вызов метода формы, который вызовет нашу процедуру. Здесь второй аргумент процедуры необходим для целей оптимизации расчетов.
Чтобы понять, насколько хорошо наше второе решение, протестируем и его. Для этого усложним пример: введем новые реквизиты и зависимости. Добавим новый реквизит Статья, значение которого будет определяться договором и видом операции. Если теперь изменить значение Вида операции, то у нас появиться два пути расчета зависимостей:
- Вид операции->Договор->Статья
- Вид операции->Статья
При условии оптимизации результат расчета будет зависеть от порядка выбранных путей. Интуитивно кажется, что выбор наиболее длинного пути верный. Действительно, для нашего примера это так, однако в общем случае это не так. Правильным вариантом будет такой порядок расчета элементов зависимостей, когда зависимые элементы идут после тех, от которых они зависят. Такой порядок в теории графов называется топологическим, а сам граф должен быть при этом однонаправленным и ациклическим.
Итак, наше второе решение с выделением общей функции не оптимально: ведь оно не может использовать топологический порядок! В следующем решении рассмотрим вариант алгоритма, построенного на описании графа зависимостей. Для работы с формой нам даже не придется придумывать где хранить этот граф, ведь можно воспользоваться связями параметров выбора в элементах формы. Однако такое решение будет нас ограничивать работой с формой. Для обеспечения общности определим хранение зависимостей для формы в ее реквизите, а для объекта - в переменной (можно воспользоваться структурой Дополнительные параметры). Полное описание такого решения представлено в статье Шаблон MVC для управляемого интерфейса.
Рассмотрим данные на примере реализации документа «Заявка на операцию» из действующей рабочей конфигурации. На диаграмме размер узла пропорционален количеству связей, синий цвет соответствует данным объекта, голубой – константным параметрам.
Однако обеспечение расчета зависимостей в самом объекте недостаточно для работы пользовательского интерфейса. Дело в том, что взаимодействие с пользователем несет дополнительную семантику, которая отражена только в работе интерфейса пользователя. Например, выбор способа оплаты через кассу или банк будет определять наличие на форме элемента выбора счета контрагента. Получается, что интерфейс отражает состояние данных в соответствии с той семантикой, которая заложена в его поведении взаимодействия с пользователем. В платформе есть возможность декларативного описания настроек элементов формы от данных объекта или контекста формы – «Условное оформление». Этот механизм в полной мере хорошо работает для оформления строк таблиц, однако применительно к остальным элементам формы весьма ограниченно и поэтому практически не применяется.
Представленный список параметров условного оформления меньше возможного списка параметров, определяющих настройку элементов формы и это еще одно ограничение этого механизма.
Распространенным простым решением для настройки формы является реализация общей процедуры что-то типа «УправлениеВидимостьюДоступностью(Форма)». Если у нас есть такая процедура, то для корректного отражения состояния в представлении формы достаточно добавить вызов нашей процедуры в конце обработчика «ПриИзменении», а также «ПриСозданииНаСервере».
На практике, решая вопрос оптимизации клиент-серверного взаимодействия, эту функцию разбивают на несколько: функция безконтекстного исполнения, выполняется и на сервере и на клиенте, и функция, требующая контекста сервера. В большинстве случаев достаточно вызова безконтекстной функции, и лишь в некоторых случаях отдельно вызывается контекстная.
В общем случае код такой процедуры может представлять собой использование операторов Если-Тогда и вспомогательных переменных типа флажков и промежуточных значений переменных. При достаточном большом количестве элементов код такой функции может быть весьма сложным, а его сопровождение и расширение - затратным. Попробуем улучшить это решение.
На входе мы имеем множество данных и элементов формы, свойства которых определяются этими данными. В общем случае отношение данных к зависимым элементам будет многие ко многим. Попробуем сперва сгруппировать код нашей процедуры по данным. Такая группировка приведет к повторению настроек элементов, зависимых от данных, что не очень удобно и потребует дополнительного контроля для исключения таких повторов. В случае необходимости расширения или изменения зависимостей весь код потребуется пересмотреть заново, ведь настройки элементов будут соотнесены с одной из возможных группировок данных, а некоторые группировки будут пропущены, т.к. код настройки элементов с их использованием уже был ранее в другой группировке.
Попробуем второй вариант: группировка элементов по данным. Здесь уже будет повторятся код, используемый в разных элементах, однако его всегда можно оптимизировать за счет использования общих функций и дополнительных переменных состояния. Этот вариант предпочтительнее первого: при добавлении нового элемента нам достаточно вставить код его настройки в конец процедуры, а при изменении для уже существующего - найти участок кода его настройки и сделать в нем необходимые изменения.
При изменении одного реквизита вызывать процедуру настройки всей формы не очень оптимально. Улучшим наше решение. Мы уже и так разделили нашу процедуру на отдельные участки кода, соответствующие настройке свойств отдельных элементов (группировка по элементам). Выделим код настройки элементов в отдельные функции – пусть это будут функции состояния. Тогда при изменении реквизитов, зная зависимость элементов от данных, можно вызвать настройку только тех элементов, свойства которых могли поменяться.
Добавим в нашу модель описание параметров, значения которых будут определятся как самими данными, так и на основе значений других параметров. Путь это будут «Параметры состояния», тогда зависимости элементов можно описать уже от этих параметров, т.е. функции состояния будут реализовывать настройку свойств элементов по параметрам состояния, от которых они зависят. Общий алгоритм расчета состояния формы по измененным данным будет такой: вначале определяются значения измененных параметров, которые определяются измененными данными; затем вычисляются зависимые от них параметры; определяются элементы формы, свойства которых зависят от измененных параметров; для каждого из найденных элементов вызывается своя функция состояния. Подробнее это решение представлено в статье Управление состоянием формы через конечный автомат.
На следующей диаграмме представлены связи данных с элементами формы на примере реализации документа Заявка на операцию из действующей конфигурации. Здесь также, как и на предыдущей диаграмме связей данных, размер узла пропорционален количеству связей, синим и голубым обозначены параметры, а желтым – элементы формы.
Решения для расчета зависимостей и состояния формы вполне хорошо зарекомендовали себя на практике. Использование языка зависимостей позволяет не только лучше взаимодействовать постановщикам задач и разработчикам, но и может служить некоторой основой самодокументации реализуемой системы. Теперь зависимости определяются не в самом коде, а в структуре, по которой реализуются алгоритмы расчета и это устраняет расхождения между описанием и исполнением.
Практика использования двух подсистем выявила их недостатки и преимущества. Главным недостатком использования двух подсистем оказалось дублирование описания реквизитов/параметров и связей между ними. Практически описание модели объекта повторяется в описании зависимостей и параметров состояния для формы. Две подсистемы создавались изначально независимо друг от друга и решали разные задачи отдельно: расчет данных объекта и расчет параметров состояния формы. При этом описание реквизитов обладает более богатой семантикой: это и определение ссылочных данных по связям параметров выбора, и проверка заполнения, и обработка заполнения, и обработчик события «ПриИзменении», и возможность перестроения графа зависимостей. Подсистема управления состоянием формы оперирует параметрами состояния, для которых поддерживаются только: выражения расчета значений и разные возможности хранения (реквизит данных, контекста или хранилище значений). Кроме того, обе подсистемы реализуют алгоритм обхода зависимостей: реквизитов – для подсистемы расчета зависимостей, параметров состояния – для подсистемы управления состоянием формы.
В новом решении «Управление состоянием» объединены возможности обеих подсистем. Здесь основными элементами расчетов являются параметры состояния. Их значения могут храниться как в данных объекта, так и в данных контекста формы или объекта, а также в хранилище значений параметров состояния. Для них поддерживаются: ссылочные данные и алгоритмы расчета ссылочных значений по связям параметров выбора, выражения значения, проверка заполнения, признак использования, обработчик «ПриИзменении». Кроме того, сами свойства параметров состояния и связей могут быть также параметризованы. Последнее позволяет описывать параметризованный граф зависимостей.
«Управление состоянием» - это не только оптимизация архитектуры, но и оптимизация производительности. В прежних подсистемах не была реализована оптимизация клиент-серверного взаимодействия и все расчеты всегда производились только в контексте сервера (на стороне back-end - как принято говорить в web разработке). Однако теперь клиент-серверное взаимодействие оптимизировано как по количеству вызовов, так и по объему данных. Конечно подсистема сама не всегда точно может определить нужный контекст исполнения и поэтому для тонкой настройки механизма будет требоваться конкретное указание контекста путем определения признака «НаСервере» у параметров состояния и у элементов (объект описания элемента формы).
Ориентируясь на описания свойств параметров состояния, таких, как «Проверка заполнения» и «Использование», которые также могут быть параметризованы, появилась возможность использования этой информации для подсистемы проверки заполнения и очистки – однако эта потенциальная возможность будет использована уже в другой подсистеме, описание которой не входит в рамки текущей статьи.
Описание модели формы заключается в описании её элементов и параметров состояния, от которых зависят эти элементы.
На примере элементов СуммаВзаиморасчетов и ВалютаВзаиморасчетов из прилагаемой демо-базы рассмотрим их настройку. Здесь реализуется следующее поведение. Если валюта документа совпадает с валютой расчетов, то сумма взаиморасчетов должна быть скрыта, т.к. значение этой суммы равно значению суммы документа и при таком условии элемент суммы взаиморасчетов будет дублировать ввод. Это же поведение дублируется для элемента табличной части ДвиженияОперацииСуммаВзаиморасчетов. Валюта взаиморасчетов при совпадении валют должна показывать заголовок, в противном случае её заголовок должен быть скрыт. В первом случае достаточно заголовка элемента Суммы взаиморасчетов, а во втором, когда элемент суммы скрыт, уже требуется заголовок элемента Валюты расчетов.
Кроме того, при заполненном значении договора контрагента элемент Валюты расчетов должен быть недоступным для редактирования, т.к. его значение полностью определяется валютой договора.
Настройка элементов СуммаВзаиморасчетов и ВалютаВзаиморасчетов:
РаботаСФормойКлиентСервер.Элемент(ЭтотОбъект, Модель, Элементы.СуммаВзаиморасчетов, "ВалютаВзаиморасчетов,ВалютаДокумента");
РаботаСФормойКлиентСервер.Элемент(ЭтотОбъект, Модель, Элементы.ВалютаВзаиморасчетов, "ВалютаВзаиморасчетов,ВалютаДокумента,ДоговорКонтрагента");
Функции состояния элементов, обеспечивающие заданное поведение:
Функция СвойстваСуммаВзаиморасчетов(Свойства, Параметры) Экспорт
Свойства.Вставить("Видимость", НЕ Параметры.ВалютаВзаиморасчетов = Параметры.ВалютаДокумента);
Возврат Истина;
КонецФункции
Функция СвойстваВалютаВзаиморасчетов(Свойства, Параметры) Экспорт
Если Параметры.ВалютаВзаиморасчетов = Параметры.ВалютаДокумента Тогда
Свойства.Вставить("ПоложениеЗаголовка", ПоложениеЗаголовкаЭлементаФормы.Авто);
Иначе
Свойства.Вставить("ПоложениеЗаголовка", ПоложениеЗаголовкаЭлементаФормы.Нет);
КонецЕсли;
Свойства.Вставить("ТолькоПросмотр", ЗначениеЗаполнено(Параметры.ДоговорКонтрагента));
Возврат Истина;
КонецФункции
Полное описание модели формы в обработчике ПриСозданииНаСервере:
На следующем слайде представлена модель данных, где синим обозначены реквизиты объекта, а желтым – элементы формы.
На следующем слайде продемонстрирован граф последовательности изменений при выборе значения контрагента (красным – измененные данные, оранжевым – зависимые элементы):
Модель объекта описывает только данные предметной области. В качестве абстракции данных в модели принято понятие Параметра состояния. Параметр состояния может определяться как ссылочное значение по связям параметров выбора, по выражению или функции значения, а также в обработчике ПриИзменении другого параметра. Значение параметра может храниться в данных объекта, контекста или в хранилище значений параметров состояния.
Описание модели содержится в структуре Модель, которую должна возвращать одноименная функция менеджера объекта. Пример описания модели объекта представлен ниже.
Важной частью реализации является возможность использовать модель в алгоритмах без участия формы объекта. На представленном примере (следующий слайд) в объекте устанавливается новая дата и валюта. Измененные реквизиты служат основой расчета зависимостей модели объекта. После возвращения данных объекта в контекст формы достаточно выполнить расчет производных параметров состояния (параметров, которые не сохраняются в объекте и значения которых определяются данными объекта или контекста).
В подсистеме поддерживается оптимизация клиент-серверного взаимодействия по объему данных и по количеству контекстных вызовов. Также оптимизирован сам расчет – расчет начинается от изменённых параметров и завершается при достижении последнего в графе зависимостей или, когда значение параметра пустое при условии обязательного заполнения.
Чтобы система работала необходимо соблюдать одно условие – граф зависимостей должен быть однонаправленным и ациклическим. Именно такой граф может обеспечить топологический порядок параметров состояния.
Диагностика ошибки в системе очень важна. Тут дело не только в том, что ошибка в алгоритме может сделать дальнейшую работу пользователя невозможной или приведет к потери введённых данных, а в том, что система вполне может продолжить работу, однако консистентность будет нарушена. Последнее обстоятельство может перечеркнуть все достоинства системы, поэтому важно отслеживать ошибки расчета зависимостей и максимально оперативно их устранять.
Для расследования причины ошибки необходимо знать не только место её возникновения, но и контекст. В декларативных системах знание контекста определяющее, т.к. поведение системы полностью определяется им. Стандартная обработка ошибки нам мало сможет помочь, поэтому в системе предусмотрена расширенная диагностика ошибок.
В процессе расчета зависимостей есть несколько потенциальных точек отказа:
- функция значения (параметр состояния);
- обработчик события «ПриИзменении» (параметр состояния);
- функция состояния (элемент формы)
В подсистеме предусмотрено дополнение описания ошибки значениями контекста возникновения. Такое дополнение позволяет точно определить источник ошибки: конкретный параметр состояния или элемент формы, а также обработчик события.
Подсистема предполагает реализацию функции Модель в модуле менеджера объекта. Кроме описания самой настройки модели необходимо описание функций состояния, значения и обработки событий ПриИзменении. Эти функции должны располагаться в общем модуле доступном для контекста клиента и сервера. При этом функции, которые требуют для своего исполнения контекста только сервера, должны быть заключены в блоке препроцессора #Если Сервер Тогда … #КонецЕсли
Структура модуля формы
Структура модели
См. описание РаботаСМодельюКлиентСервер.Модель.
Пример создания модели в демо базе модуль менеджера Документ.ЗаявкаНаОперацию.Модель.
См. описание РаботаСМодельюКлиентСервер.Параметр
В большинстве случаев создавать отдельно параметр состояния не требуется. Параметры состояния создаются в модели неявным образом при добавлении новых связей. Однако если настройка параметра состояния по умолчанию вас не устраивает, то ее можно изменить непосредственно, обратившись к параметру через структуру модели Модель[ИмяПараметра] или безопасным образом через функцию РаботаСМодельюКлиентСервер.Параметр[ИмяПараметра].
См. описание РаботаСМодельюКлиентСервер.Связь
В описании модели связи решают все! Формально модель - это параметры состояния и их связи (для формы добавлены элементы), однако описывать параметры состояния отдельно в большинстве случаев не нужно: достаточно добавить описания связей. Система сама, на основе анализа контекста, определит описания параметров. Такой подход имеет одно ограничение: имена реквизитов объекта и формы не должны пересекаться (не относится к реквизитам табличных частей).
Ограничение на однонаправленность и ацикличность связей может быть для некоторых требований слишком жестким. Например, счет контрагента определяется по связям как Валюта документа->Счет контрагента, однако пользователю удобно выбрать счет в нужной валюте. В таком случае указанная связь оказывается слабой.
Концепция слабой связи работает для интерактивного выбора. При интерактивном выборе значения слабые связи не используются для установления условий отбора. Такое поведение интерфейса позволяет сделать выбор значения, которое будет противоречить условию существующей связи. Чтобы соблюдалась консистентность данных в обработчике события ПриИзменении необходимо определить алгоритм установки правильного значения связи. Пример такого обработчика ПриИзменении можно посмотреть в демо: МодельЗаявкаНаОперацию.ПриИзмененииСчетКонтрагента.
При определении слабой связи может возникнуть вопрос: в какую сторону направить связь, а в какую использовать обработчик ПриИзменении? Ответ на этот вопрос определяется порядком расчета модели. Этот порядок определяет направление связи. Если требуется для интерактивного выбора отключить ограничение связи, то используется слабая связь, а в обработчике ПриИзменении выбранного параметра определяется алгоритм расчета значения связанного ведущего параметра.
Вторым вопросом может быть: как избежать зацикливания? Для этого в обработчике необходимо проверить, что связанный ведущий параметр еще не был рассчитан. Поскольку согласно топологическому порядку именно ведущий параметр идет первым в расчете, то если он не был рассчитан и означает, что значение было выбрано без учета слабой связи.
В следующем обработчике анализируется направление слабой связи, которое зависит от типа операции. Для внешней операции это Валюта документа->Счет контрагента, а для внутренней (конвертация валюты) – Валюта взаиморасчетов->Счет контрагента. И конечно же, если среди рассчитанных реквизитов уже есть валюта, то никакой обратной связи не требуется!
Процедура ПриИзмененииСчетКонтрагента(Контекст, Объект, ДанныеСтроки, Реквизит, ИзмененныеРеквизиты, РассчитанныеРеквизиты) Экспорт
Если Объект.ТипОперацииБюджетирование = Перечисления.ТипыОперацийБюжетирование.КонвертацияВалюты Тогда
ИмяРеквизитаВалюты = "ВалютаВзаиморасчетов";
Иначе
ИмяРеквизитаВалюты = "ВалютаДокумента";
КонецЕсли;
Если РассчитанныеРеквизиты[ИмяРеквизитаВалюты] = Истина ИЛИ НЕ ЗначениеЗаполнено(Объект.СчетКонтрагента) Тогда
Возврат;
КонецЕсли;
РаботаСМодельюКлиентСервер.УстановитьЗначение(Контекст, ИмяРеквизитаВалюты, , ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Объект.СчетКонтрагента , "Валюта"), ИзмененныеРеквизиты);
КонецПроцедуры
Иногда удобно одни и те же параметры использовать в разных связях. Например, связь Контрагент->Счет контрагента определена для внешней операции, однако для внутренней операции указанная связь как и сам реквизит Контрагент становятся недействительными. Для разного типа операции существуют разные связи: Контрагент->Счет контрагента – для внешней, Организация->Счет контрагента – для внутренней.
В предыдущей версии подсистемы существовала возможность перестроения графа зависимостей, однако в новой подсистеме это достигается путем параметризации. Так для первой связи можно указать, что свойство «Использование» определяется параметром ЭтоВнешняяОперация, а для второй – ЭтоВнутренняяОперация. Аналогично свойство «Использование» определяется и для реквизита Контрагент.
Механизм параметризации позволяет описывать динамическую модель объекта, где свойства связей и параметров сами описываются через другие параметры.
В управляемом интерфейсе элементы формы имеют иерархическую структуру в виде дерева. Такая организация элементов используется для компоновки формы. С одной стороны, это достаточно простая организация позволяет эффективно описывать компоновку интерфейса, а с другой – это ограничение. В ситуации, когда выделяются группы элементов, не находящихся на одном уровне подчинения или в одной ветке иерархии элементов, для их совместной настройки необходимо дублировать эту настройку для каждого элемента или группы в одной иерархии.
В рамках подсистемы управления состоянием формы реализовано описание элементов и их групп. Здесь под группами понимается не родительский элемент с его подчиненными элементами, а список элементов, поведение которых настраивается общим способом через настройку группы.
Деятельность отдельного пользователя может быть очень специализированной. В этом суть разделения труда, когда одни работают с поставщиками, другие с покупателями, третьи рассчитывают зарплату. Такая специализация приводит к использованию ограниченного набора данных и повторяемости выбора для одних и тех же условий, с которыми имеет дело пользователь. Накопление статистики и предугадывание того, какое значение будет выбрано пользователем в следующем реквизите – позволяет сделать систему адаптивной под рабочие предпочтения пользователя. Такая адаптивность системы не только увеличивает производительность работы пользователя, но и уменьшает вероятность ошибки ввода данных в систему.
В основе адаптивности ввода лежит платформенный механизм истории выбора значений при вводе. У него есть свои ограничения. Так, если выбор значения переопределен в модуле менеджера (ОбработкаПолученияДанныхВыбора), то платформенный механизм не будет учитывать новые критерии выбора. Второе ограничение связано с тем, что платформенный механизм ориентируется на неизменность данных: т.е., например, если для данной организации и контрагента выбран договор один раз, то и в следующий раз для тех же организации и контрагента можно выбрать тот же договор, однако если в самих данных для договора поменять значение контрагента, то платформенный механизм никак это не учтет и позволит сделать уже неверный выбор. Можно рассмотреть пример и попроще. Пусть в параметрах выбора определено значение пометки удаления Ложь (отбор только не помеченных на удаление элементов), тогда если элемент пометить, то платформа не только позволит сделать выбор из истории, но и никак не предупредит, что выбранный элемент помечен на удаление!
К сожалению список истории ввода никак нельзя переопределить из встроенного языка. Сама 1С для таких случаев, где действуют ограничения, рекомендует отключать историю выбора. Однако этот механизм повышает удобство работы пользователя и отказываться от него нерационально. Решением здесь будет продолжать использовать механизм там, где он востребован, а для разрешения ограничений использовать дополнительную проверку ввода. Тогда сама платформа будет продолжать предлагать пользователю значения для быстрого ввода исходя из накопленной статистики, однако на встроенном языке будет выполняться дополнительная проверка, которая не позволит ввести значение, которое перестало быть валидным.
Механизм проверки введенного значения активизируется для всех полей ввода с установленным свойством элемента формы ИсторияВыбораПриВводе равным Авто. Такая проверка может привести к нежелательному поведению системы. Например, мы хотим ввести в поле Сумма расчетов свое значение, тогда при проверке система рассчитает значение через кросс-курс от суммы документа и заменит им наше. Чтобы такого не происходило, необходимо для полей с числовыми данными отключать историю выбора через установку значения НеИспользовать.
Копирование информационного объекта - есть ничто иное как ввод информации для нового объекта по данным исходного. При этом возможна ситуация, когда данные валидные в прошлом перестали соответствовать текущим параметрам выбора. В таком случае необходимо также, как и при вводе значения из истории, выполнить проверку данных на соответствие текущим ограничениям.
Следующий код демонстрирует проверку данных нового объекта через метод ОбновитьДанные.
Процедура ПриКопировании(ОбъектКопирования)
Дата = ТекущаяДата();// дата требуется для расчета кросс-курса
РаботаСМоделью.Инициализировать(ЭтотОбъект);// конструируется модель объекта
РаботаСМоделью.РассчитатьПроизводныеПараметры(ЭтотОбъект);// расчет не сохраняемых параметров
РаботаСМоделью.ОбновитьДанные(ЭтотОбъект);// проверка сохраняемых данных на соответствие модели
КонецПроцедуры
Изменение данных объекта приводит к пересчету связанных зависимостью параметров. Однако бывают ситуации, когда параметры не входят в цепочку зависимостей или, когда требуется перенести данные объекта в контекст формы. В первом случае источником изменения данных может быть внешнее событие, например, это может быть событие записи объекта. В обоих случаях для восстановления значений расчетных параметров, которые не сохраняются в объекте, требуется выполнить их расчет.
Например, при проведении документа меняется его состояние, значения которого хранится в регистре. После проведения документа требуется заново определить значение параметра «Состояние».
Второй пример с переносом данных объекта в контекст формы можно посмотреть в демо-базе в обработчике команды «Изменить данные объекта». Здесь происходит вначале конвертация данных формы в объект, затем производятся изменения данных объекта и объект возвращается в контекст формы (см. Модель объекта и программный интерфейс).
Расчет Зависимости от договора
Сценарий к разделу Модель управления формы.
Используемые зависимости модели объекта:
...
// Связи параметров выбора для договора
РаботаСМодельюКлиентСервер.Связь(Контекст, Модель, "ДоговорКонтрагента", "Организация", "Отбор.Организация");
РаботаСМодельюКлиентСервер.Связь(Контекст, Модель, "ДоговорКонтрагента", "Контрагент", "Отбор.Контрагент");
// Связь валюты расчетов от договора по параметру ДоговорКонтрагента. На связь наложено условие использования: для внешней операции (Оплата поставщику)
РаботаСМодельюКлиентСервер.Связь(Контекст, Модель, "ВалютаВзаиморасчетов", "ДоговорКонтрагента",,, Новый Структура("Использование", "ЭтоВнешняяОперация"));
// Следующие связи определяют расчет суммы взаиморасчетов от суммы документа, кросс-курса
РаботаСМодельюКлиентСервер.Связь(Контекст, Модель, "СуммаВзаиморасчетов", "СуммаДокумента");
РаботаСМодельюКлиентСервер.Связь(Контекст, Модель, "СуммаВзаиморасчетов", "КроссКурс");
// Аналогичные связи для табличного реквизита сумма взаиморасчетов
РаботаСМодельюКлиентСервер.Связь(Контекст, Модель, "ДвиженияОперации.СуммаВзаиморасчетов", "ДвиженияОперации.Сумма");
РаботаСМодельюКлиентСервер.Связь(Контекст, Модель, "ДвиженияОперации.СуммаВзаиморасчетов", "КроссКурс");
// Связи, определяющие расчет кросс-курса от даты и валюты документа, валюты расчетов
РаботаСМодельюКлиентСервер.Связь(Контекст, Модель, "КроссКурс", "Дата");
РаботаСМодельюКлиентСервер.Связь(Контекст, Модель, "КроссКурс", "ВалютаДокумента");
РаботаСМодельюКлиентСервер.Связь(Контекст, Модель, "КроссКурс", "ВалютаВзаиморасчетов");
...
Название: |
Заполнение договора контрагента |
Описание: |
После выбора договора система заполняет валюту и сумму расчетов. Элемент формы Валюта расчетов становится недоступным для редактирования. |
Предусловия: |
В документе заполнены: операция Оплата поставщику, Организация, Контрагент, значение Валюты документа равна Валюте расчетов рубли. Договор контрагента не заполнен. |
Основной поток: |
|
Требования: Платформа 8.3.14 и выше для работы в тонком и веб клиенте.
Подсистема |
Метаданные |
Назначение |
|
Общий модуль: |
|
БСП.БазоваяПодсистема |
|
|
УправлениеСостоянием |
Общий |
|
|
ОбщийКлиентСервер |
|
|
Обеспечивает нестандартную обработку выбора |
|
|
Используется для описания запроса в подсистеме работы с данными выбора |
|
|
РаботаСМоделью |
|
|
РаботаСМодельюКлиентСервер |
|
|
РаботаСФормой |
|
|
РаботаСФормойКлиентСервер |
|
Демо |
Документ.ЗаявкаНаОперацию |
|
|
МодельЗаявкаНаОперацию |
Описание модели документа ЗаявкаНаОперацию |
|
Справочники: Контрагенты, ДоговорыКонтрагентов, Организации |
|
Порядок внедрения:
- Выгрузить конфигурацию из демо-базы
- Объединить с целевой конфигурацией с отбором по подсистеме УправлениеСостоянием
Подсистема "Управление состоянием" расширяет возможности определять поведение объекта и его интерфейса на основе декларативного описания параметров состояния и связей. Единая подсистема решает задачу шаблона MVC, а также позволяет описывать сложное поведение интерфейса, задавать правила проверки заполнения и очистки данных.
Специалисты по проектированию взаимодействия получают возможность использовать язык описания зависимостей при постановке задач на реализацию интерфейса. Работа алгоритмов подсистемы полностью определяется этими зависимостями и фактически самодокументируется через них. Пользователи при этом получают интерфейс, соответствующий заложенному специалистом описанию работы. А работа алгоритмов по изменению состояния и оптимизация клиент-серверного взаимодействия позволяет строить производительные интерфейсы с достаточно сложной логикой поведения.
Программисты могут использовать одни и те же алгоритмы расчета модели объекта как через пользовательский интерфейс, так и через программный. Это позволяет не только экономить ресурсы разработчика, но и привлеченным программистам использовать объекты незнакомой системы таким же образом, как это делает пользователь, руководствующийся инструкцией.
Бизнес получает продукт - разработанную бизнес-систему с лучшими характеристиками взаимодействия, сопровождаемости и развития. Система гарантирует математически точную работу интерфейса, расчет зависимых данных, проверку заполнения и очистку.
Проект в формате EDT опубликован на gihub.
Лучшее объяснение идеи MVC:
- Охота на мифический MVC. Обзор, возвращение к первоисточникам и про то, как анализировать и выводить шаблоны самому
- Охота на мифический MVC. Построение пользовательского интерфейса
Другие материалы по теме в статьях Infostart.ru:
- КоДан: Контроль ввода данных и доступа к данным [БП, УТ, ЗУП, УНФ, ERP] (платное решение)
- Новый способ программной настройки условного оформления
- Управление свойствами элементов формы. Хранение копии данных реквизитов
- Запись значения в поле ввода/формы со срабатыванием события ПриИзменении
- Помощник заполнения
- Методика обновления формы объекта данных при изменении объекта
- Модель объекта
- Гибкое управление свойствами доступности элементов управления
- Динамическое управление видимостью и доступностью элементов форм (платное решение)
Смотрите также разбор примера.