При разработке сложных интерфейсов на управляемых формах 1С часто возникает необходимость динамически добавлять элементы: страницы (вкладки), поля ввода или кнопки. Однако начинающие разработчики часто сталкиваются с проблемой: элементы создаются "на лету", но после записи документа и закрытия формы они бесследно исчезают вместе с введенными данными. В этой статье мы подробно разберем, как правильно реализовать динамический интерфейс, чтобы он корректно работал и сохранял информацию в базе данных.
Для начала проанализируем, почему программно созданные элементы пропадают. В системе 1С:Предприятие 8 программные элементы формы (страницы, декорации, кнопки) существуют только в оперативной памяти текущего сеанса работы с формой. Как только форма закрывается, всё, что было создано через коллекцию Элементы.Добавить(), уничтожается. Платформа не запоминает структуру формы, если она не описана в конфигураторе статически.
Чтобы форма выглядела одинаково при каждом открытии, мы должны повторно выполнять код по созданию элементов в процедуре ПриСозданииНаСервере. Если же элементы должны появляться в ответ на действия пользователя (например, при выборе значения в поле), нам нужно сохранить текущее "состояние" (количество страниц или список менеджеров) в реквизитах самого объекта, чтобы при следующем открытии код "знал", что именно нужно восстановить.
Рассмотрим ситуацию, когда при изменении списка менеджеров в документе должны создаваться соответствующие вкладки с полями ввода. Рассмотрим типичную ошибку: создание элементов только в событии ПриИзменении. Если мы так поступим, то при повторном открытии документа форма будет пустой. Выясним причину: событие ПриИзменении уже не сработает автоматически, а в процедуре ПриСозданииНаСервере кода по отрисовке элементов нет.
Правильный подход заключается в выносе логики отрисовки в отдельную процедуру, которую мы будем вызывать из обоих мест. Рассмотрим пример структуры кода:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Восстанавливаем динамический интерфейс на основе данных объекта
ОбновитьДинамическиеСтраницы();
КонецПроцедуры
&НаСервере
Процедура МенеджерыПриИзмененииНаСервере()
// Перерисовываем интерфейс при изменении данных
ОбновитьДинамическиеСтраницы();
КонецПроцедуры
&НаСервере
Процедура ОбновитьДинамическиеСтраницы()
// Здесь располагается логика удаления старых и создания новых элементов
// Используем Элементы.Добавить(...) и ИзменитьРеквизиты(...)
КонецПроцедуры
Часто разработчики используют метод ИзменитьРеквизиты() для создания новых полей. Важно понимать: созданные таким образом реквизиты формы никак не связаны с базой данных автоматически. Это лишь временные переменные в памяти формы. Чтобы данные из этих полей (например, список поставщиков на странице менеджера) попали в документ, необходимо выполнить привязку к реальным метаданным.
Проанализируем два способа решения этой задачи:
ПоставщикиПоМенеджерам), где храним связь "Менеджер — Поставщик". При создании динамического поля на форме мы указываем его ПутьКДанным напрямую к строке этой табличной части.ИзменитьРеквизиты, нам придется вручную переносить значения из этих реквизитов в объект 1С перед записью (в обработчике ПередЗаписьюНаСервере) и обратно при открытии формы.Выясним, как связать программное поле с данными так, чтобы платформа сама заботилась о сохранении. Если у вас в объекте есть табличная часть, вы можете обращаться к её данным через индекс или через связь с текущей строкой. Посмотрим на пример кода, где мы добавляем поле ввода и привязываем его к реквизиту объекта:
// Предположим, у нас есть ТЧ "Менеджеры" в документе
Для Каждого СтрокаТЧ Из Объект.Менеджеры Цикл
ИмяЭлемента = "ПолеМенеджера_" + СтрокаТЧ.НомерСтроки;
НовыйЭлемент = Элементы.Добавить(ИмяЭлемента, Тип("ПолеФормы"), ГруппаСтраница);
НовыйЭлемент.Вид = ВидПоляФормы.ПолеВвода;
// Прямая привязка к данным объекта в табличной части
// Индексация в ПутьКДанным начинается с 0
НовыйЭлемент.ПутьКДанным = "Объект.Менеджеры[" + (СтрокаТЧ.НомерСтроки - 1) + "].Менеджер";
КонецЦикла;
Важный момент: использование прямой индексации (Объект.Менеджеры[0]) может быть рискованным, если строки в табличной части могут удаляться или перемещаться. В таких случаях надежнее создавать реквизиты формы, которые будут служить "прослойкой".
Метод ИзменитьРеквизиты является ресурсоемким. При каждом его вызове форма полностью перегенерируется на сервере. Если вызывать его в цикле для каждого нового поля, интерфейс начнет заметно "подтормаживать".
Проанализируем правильный алгоритм работы с этим методом:
РеквизитыДляДобавления.РеквизитФормы.ИзменитьРеквизиты(РеквизитыДляДобавления).Элементы.Добавить() и привязываем их к созданным реквизитам.Также не забывайте про массив РеквизитыДляУдаления. Если состав страниц меняется (например, менеджеров стало меньше), необходимо обязательно удалять старые реквизиты формы, иначе при попытке добавить реквизит с тем же именем система выдаст ошибку.
Если на динамических страницах нужны кнопки (например, "Добавить поставщиков"), команды для них также нужно создавать программно. Посмотрим на пример:
ИмяКоманды = "КомандаДействия_" + Индекс;
НоваяКоманда = ЭтаФорма.Команды.Добавить(ИмяКоманды);
НоваяКоманда.Действие = "ОбщийОбработчикНажатияКнопки";
НоваяКоманда.Заголовок = "Выполнить действие";
НоваяКнопка = Элементы.Добавить("Кнопка_" + Индекс, Тип("КнопкаФормы"), Группа);
НоваяКнопка.ИмяКоманды = ИмяКоманда;
В процедуре ОбщийОбработчикНажатияКнопки(Команда), которая должна быть описана в модуле формы с директивой &НаКлиенте, мы можем определить, какая именно кнопка была нажата, анализируя параметр Команда.Имя.
Мы проанализировали основные сложности динамического построения интерфейса в 1С. Подведем итоги: чтобы данные не пропадали, интерфейс должен пересоздаваться в ПриСозданииНаСервере, а пути к данным элементов должны вести либо к реквизитам объекта (ТЧ, доп. реквизиты), либо к реквизитам формы, значения которых вы сознательно синхронизируете с объектом базы данных.
Однако, прежде чем приступать к сложному программному коду, всегда стоит рассмотреть классическую схему. В большинстве случаев задачу можно решить стандартными табличными частями и статическими элементами формы. Это упростит поддержку кода и повысит стабильность работы системы. Программное создание элементов оправдано только тогда, когда интерфейс должен кардинально меняться в зависимости от внешних настроек, которые невозможно предусмотреть на этапе проектирования.