Работа с управляемыми формами в 1С:Предприятие 8.3 требует гибкости — часто возникает необходимость динамически изменять структуру формы без перезагрузки конфигурации. Один из самых востребованных сценариев — программное добавление реквизита в уже существующую форму. Это может понадобиться при адаптации интерфейса под специфические задачи пользователя, интеграции с внешними системами или реализации сложной бизнес-логики.
В отличие от обычных форм, где реквизиты добавляются статически в конфигураторе, управляемые формы позволяют манипулировать своей структурой "на лету". Однако этот процесс имеет ряд нюансов: от выбора правильного метода (ДобавитьРеквизит() vs ручное создание элементов) до обработки событий и сохранения данных. В этой статье мы разберём все аспекты — от базовых примеров до продвинутых техник с учётом особенностей платформы 8.3.20+, где появились новые возможности работы с динамическими реквизитами.
Почему нельзя просто добавить реквизит в конфигураторе?
На первый взгляд может показаться, что проще изменить форму в конфигураторе и обновить базу. Однако есть ситуации, когда программное добавление реквизита становится единственным решением:
- 🔄 Динамические формы — когда состав полей зависит от прав пользователя, настроек системы или данных документа (например, в форме заказа могут появляться дополнительные поля при выборе определённого контрагента).
- 🔌 Интеграции — при обмене данными с внешними системами, когда структура формы должна адаптироваться под входящие данные (например, JSON с произвольными полями).
- 📦 Расширения конфигурации — когда нельзя модифицировать исходную конфигурацию, но нужно добавить функциональность.
- 🧪 Тестирование гипотез — временное добавление реквизитов для проверки новых механизмов без внесения изменений в конфигурацию.
Кроме того, программное добавление реквизитов позволяет создавать универсальные обработки, которые могут работать с разными конфигурациями без доработок. Например, одна обработка для выгрузки данных в Excel может динамически добавлять поля в форму настройки выгрузки в зависимости от структуры источника.
Базовый метод: ДобавитьРеквизит()
Самый простой способ добавить реквизит в управляемую форму — использовать метод ДобавитьРеквизит(). Он доступен для объекта УправляемаяФорма и позволяет создать новый реквизит с указанием типа, имени и других параметров. Пример минимального кода:
Процедура ДобавитьНовыйРеквизит(Форма)
Форма.ДобавитьРеквизит("НовыйРеквизит",
Новый ОписаниеТипов("Строка", Новый КвалификаторыСтроки(255)),
Новый РеквизитФормы("НовыйРеквизит", Новый ОписаниеТипов("Строка")));
КонецПроцедуры
Здесь мы добавляем строковый реквизит с ограничением длины в 255 символов. Важно отметить, что метод ДобавитьРеквизит() работает только до открытия формы — если форма уже отображена, добавление реквизита вызовет ошибку. Это логично, ведь изменение структуры формы на лету может привести к неконсистентному состоянию интерфейса.
Чтобы проверить, можно ли добавлять реквизиты в текущий момент, используйте свойство Форма.Модифицирована. Если оно равно Истина, изменения структуры запрещены.
| Параметр метода | Описание | Пример значения |
|---|---|---|
Имя |
Идентификатор реквизита (латиница, без пробелов) | "CustomField1" |
ТипЗначения |
Описание типа данных реквизита | Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(10, 2)) |
РеквизитФормы |
Объект с дополнительными свойствами реквизита | Новый РеквизитФормы("MyField", ТипЗначения) |
Владелец (опц.) |
Указывает владельца реквизита (например, группу реквизитов) | Форма.Элементы.Группа1 |
После добавления реквизита его нужно привязать к элементу формы (поле ввода, флажок и т.д.), иначе он будет существовать только в памяти формы, но не будет виден пользователю. Для этого используется метод ДобавитьЭлемент() или настройка через свойство Значение существующего элемента.
Продвинутые техники: динамическое создание элементов
Метод ДобавитьРеквизит() решает только половину задачи — он создаёт реквизит в логической структуре формы, но не отображает его на экране. Для полноценной работы нужно динамически создать элемент управления и связать его с реквизитом. Рассмотрим пример добавления текстового поля:
Процедура ДобавитьПолеВвода(Форма, ИмяРеквизита, Заголовок, ЗначениеПоУмолчанию = "")
// 1. Добавляем реквизит
Форма.ДобавитьРеквизит(ИмяРеквизита,
Новый ОписаниеТипов("Строка"),
Новый РеквизитФормы(ИмяРеквизита, Новый ОписаниеТипов("Строка")));
// 2. Создаём элемент формы
НовыйЭлемент = Форма.Элементы.Добавить(ИмяРеквизита + "_Элемент",
Тип("ПолеВвода"),
Новый ПолеВвода(ИмяРеквизита));
// 3. Настраиваем свойства элемента
НовыйЭлемент.Заголовок = Заголовок;
НовыйЭлемент.Позиция = Новый ПозицияЭлементаФормы(10, 10, 200, 25);
НовыйЭлемент.Значение = ЗначениеПоУмолчанию;
// 4. Связываем элемент с реквизитом
НовыйЭлемент.ИмяДанных = ИмяРеквизита;
КонецПроцедуры
Ключевые моменты этого подхода:
- 🔗 Связь реквизит-элемент устанавливается через свойство
ИмяДанных. Если его не указать, элемент не будет связан с данными формы. - 📏 Позиционирование элемента задаётся в пикселях относительно родительского контейнера. Для адаптивных форм лучше использовать привязку к другим элементам.
- 🔄 Типы элементов могут быть любыми:
ПолеВвода,Флажок,ПолеВыбора,Таблицаи др.
Как добавить элемент в существующую группу?
Для добавления элемента в группу (например, Панель или Страница) укажите родительский элемент в методе Добавить():
НовыйЭлемент = Форма.Элементы.Добавить(
"НовыйЭлемент",
Тип("ПолеВвода"),
Форма.Элементы.Группа1 // Родительский элемент
);
Это автоматически разместит элемент внутри группы с учётом её layouts.
Для сложных форм с большим количеством динамических элементов рекомендуется создавать шаблоны разметки заранее (например, в виде невидимых контейнеров) и уже в них добавлять новые элементы. Это упрощает управление позиционированием и избегает наложения элементов друг на друга.
Работа с типами данных и ограничениями
При добавлении реквизита критически важно правильно указать его тип. Платформа 1С:Предприятие поддерживает все базовые типы данных, а также сложные типы вроде СтрокаФиксированнойДлины или ЧислоСФиксированнойТочностью. Рассмотрим примеры объявления разных типов:
// 1. Простое число
Форма.ДобавитьРеквизит("Количество",
Новый ОписаниеТипов("Число"));
// 2. Число с фиксированной точностью (2 знака после запятой)
Форма.ДобавитьРеквизит("Сумма",
Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2)));
// 3. Дата
Форма.ДобавитьРеквизит("ДатаПоступления",
Новый ОписаниеТипов("Дата"));
// 4. Ссылка на справочник
Форма.ДобавитьРеквизит("Контрагент",
Новый ОписаниеТипов("СправочникСсылка.Контрагенты"));
// 5. Перечисление
Форма.ДобавитьРеквизит("Статус",
Новый ОписаниеТипов("ПеречислениеСсылка.СтатусыДокументов"));
Особое внимание стоит уделить ссылочным типам (справочники, документы, планы видов характеристик). При их использовании необходимо:
- Убедиться, что тип существует в текущей конфигурации (иначе будет ошибка выполнения).
- Для элементов
ПолеВыборауказать свойствоВыборИзСписка = Истина, если нужно разрешить выбор из справочника. - Обработать случай, когда справочник пуст или недоступен для пользователя.
Убедиться, что справочник существует в конфигурации|Проверить права пользователя на чтение справочника|Обработать случай пустого справочника|Настроить фильтр выбора (если нужен)|Указать форму выбора (по умолчанию или кастомную)-->
Для типов ТаблицаЗначений или ДеревоЗначений процесс усложняется — помимо добавления реквизита нужно динамически формировать структуру колонок. Пример:
// Добавляем реквизит типа "ТаблицаЗначений"
ТаблицаТип = Новый ОписаниеТипов("ТаблицаЗначений");
Форма.ДобавитьРеквизит("ДинамическаяТаблица", ТаблицаТип);
// Настраиваем структуру таблицы
Таблица = Форма.ДинамическаяТаблица;
Таблица.Колонки.Добавить("Наименование", Новый ОписаниеТипов("Строка"));
Таблица.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число"));
Обработка событий и сохранение данных
Динамически добавленные реквизиты участвуют в стандартном жизненном цикле формы, но есть нюансы:
⚠️ Внимание: Если реквизит добавлен программно после вызова события ПриСозданииНаСервере, он не будет сохранён в данных формы при записи. Все динамические реквизиты должны создаваться на этапе инициализации формы.
Для обработки изменений в динамических реквизитах используются стандартные события формы:
Процедура ДинамическаяТаблицаПриИзменении(Элемент)
// Обработка изменения в динамически созданной таблице
Сообщить("Изменена строка: " + Элемент.ТекущиеДанные.Наименование);
КонецПроцедуры
Процедура НовыйРеквизитПриИзменении(Элемент)
// Обработка изменения в динамически добавленном поле
Если НЕ ЗначениеЗаполнено(Элемент.Значение) Тогда
Элемент.Подсказка = "Поле обязательно для заполнения!";
Иначе
Элемент.Подсказка = "";
КонецЕсли;
КонецПроцедуры
Для сохранения данных динамических реквизитов в объект (документ, справочник и т.д.) нужно:
- Добавить соответствующие реквизиты в объект до вызова формы (или использовать универсальные реквизиты типа
ХранилищеЗначения). - В обработчике
ПередЗаписьюскопировать значения из формы в объект:
Процедура ПередЗаписью(Отказ, ПараметрыЗаписи)
Объект.ДополнительноеПоле1 = ЗначениеВСтрокуВнутреннее(Форма.НовыйРеквизит);
Объект.ДополнительноеПоле2 = Форма.ДинамическаяТаблица.Выгрузить();
КонецПроцедуры
Альтернативный подход — использование ХранилищеЗначения для произвольных данных:
// При открытии формы
Хранилище = Новый ХранилищеЗначения(Объект.ДополнительныеДанные);
Форма.ДинамическиеДанные = Хранилище.Восстановить();
// При записи
Объект.ДополнительныеДанные = Новый ХранилищеЗначения(Форма.ДинамическиеДанные);
Ошибки и их решения
При работе с динамическими реквизитами разработчики часто сталкиваются с типичными ошибками. Рассмотрим самые распространённые и способы их исправления:
| Ошибка | Причина | Решение |
|---|---|---|
Реквизит с таким именем уже существует |
Повторное добавление реквизита с тем же именем | Проверять существование реквизита через Форма.Реквизиты.Найти(Имя) |
Невозможно изменить форму: она уже отображена |
Попытка добавить реквизит после открытия формы | Выносить добавление реквизитов в ПриСозданииНаСервере |
Тип не найден: СправочникСсылка.НесуществующийСправочник |
Ошибка в имени типа или справочник удалён | Проверять существование типа через Метаданные.Справочники.Найти() |
| Элемент не отображается на форме | Не указано свойство ИмяДанных или ошибка в позиционировании |
Проверять связь элемента с реквизитом и координаты размещения |
Значение не является значением объекта |
Попытка записать значение неверного типа в реквизит | Контролировать типы данных при присвоении значений |
Для отладки динамических форм полезно использовать следующие приёмы:
- 🐞 Вывод в отладочное окно:
Сообщить(Форма.Реквизиты.ВыгрузитьКолонку("Имя")); - 🔍 Проверка структуры формы через
Форма.ОписаниеОпций() - 📸 Скриншот элементов: временно сделать элементы видимыми с ярким фоном для проверки позиционирования.
Все динамические изменения формы должны проходить в обработчике ПриСозданииНаСервере или ПриАктивизацииСтраницы (для страничных форм). Попытка изменить структуру формы в других событиях (например, ПриОткрытии) приведёт к ошибкам.
Практические примеры использования
Рассмотрим реальные сценарии, где программное добавление реквизитов оказывается наиболее полезным.
Пример 1: Динамические поля в документе "Заказ клиента"
Задача: в зависимости от выбранного клиента в заказе должны появляться дополнительные поля (например, "Номер договора" для юридических лиц или "Дату рождения" для физических).
Процедура КонтрагентПриИзменении(Элемент)
Если Элемент.Значение.ЭтотОбъект.ЭтоГруппа() Тогда
Возврат;
КонецЕсли;
// Удаляем старые динамические поля, если они были
Если Форма.Реквизиты.Найти("НомерДоговора") <> Неопределено Тогда
Форма.УдалитьРеквизит("НомерДоговора");
Форма.Элементы.Удалить("НомерДоговора_Элемент");
КонецЕсли;
// Добавляем новое поле в зависимости от типа контрагента
Если Элемент.Значение.ЮрФизЛицо = Перечисления.ТипыКонтрагентов.ЮридическоеЛицо Тогда
ДобавитьПолеВвода(Форма, "НомерДоговора", "Номер договора", "");
ИначеЕсли Элемент.Значение.ЮрФизЛицо = Перечисления.ТипыКонтрагентов.ФизическоеЛицо Тогда
ДобавитьПолеВвода(Форма, "ДатаРождения", "Дата рождения", ТекущаяДата());
КонецЕсли;
КонецПроцедуры
Пример 2: Интеграция с внешней системой
Задача: при получении JSON от внешнего сервиса нужно динамически создать поля в форме для отображения дополнительных данных.
Процедура ЗагрузитьДополнительныеДанные(JSONСтрока)
Данные = JSON.Прочитать(JSONСтрока);
Для Каждого Поле Из Данные Цикл
ИмяПоля = "ДопПоле_" + Поле.Ключ;
Если Форма.Реквизиты.Найти(ИмяПоля) = Неопределено Тогда
Форма.ДобавитьРеквизит(ИмяПоля, Новый ОписаниеТипов("Строка"));
НовыйЭлемент = Форма.Элементы.Добавить(ИмяПоля + "_Элемент",
Тип("ПолеВвода"),
Новый ПолеВвода(ИмяПоля));
НовыйЭлемент.Заголовок = Поле.Ключ;
НовыйЭлемент.Значение = Поле.Значение;
Иначе
Форма[ИмяПоля] = Поле.Значение;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Пример 3: Настройка отчёта пользователем
Задача: позволить пользователю самостоятельно добавлять поля в форму настройки отчёта.
Процедура ДобавитьПользовательскоеПоле(Имя, Тип, Заголовок)
// Добавляем реквизит
ТипЗначения = Новый ОписаниеТипов(Тип);
Форма.ДобавитьРеквизит("ПользПоле_" + Имя, ТипЗначения);
// Добавляем элемент в специальную группу
Группа = Форма.Элементы.ПользовательскиеПоля;
НовыйЭлемент = Форма.Элементы.Добавить("ПользПоле_" + Имя + "_Элемент",
Тип("ПолеВвода"),
Группа);
НовыйЭлемент.Заголовок = Заголовок;
НовыйЭлемент.ИмяДанных = "ПользПоле_" + Имя;
// Сохраняем настройки для будущих открытий формы
НастройкиПользователя = ПолучаемНастройкиПользователя();
НастройкиПользователя.Добавить(Имя, Новый Структура("Тип,Заголовок", Тип, Заголовок));
СохранитьНастройкиПользователя(НастройкиПользователя);
КонецПроцедуры
Оптимизация производительности
Динамическое изменение форм может влиять на производительность, особенно если реквизитов много или они добавляются часто. Следующие рекомендации помогут минимизировать накладные расходы:
- ⚡ Кэширование структуры: Если форма открывается часто с одинаковым набором динамических полей, кэшируйте их описание (например, в регистре сведений) и восстанавливайте при открытии.
- 🗑️ Удаление ненужных реквизитов: Всегда удаляйте динамические реквизиты методом
УдалитьРеквизит(), если они больше не нужны. Это уменьшает объём данных формы. - 🔄 Ленивая загрузка: Добавляйте реквизиты только когда они действительно нужны (например, при активации определённой закладки).
- 📊 Группировка элементов: Размещайте динамические элементы в скрытых контейнерах и показывайте их только при необходимости.
Для форм с большим количеством динамических элементов (более 20) имеет смысл рассмотреть альтернативные подходы:
- Использование таблиц: Вместо множества отдельных полей использовать таблицу значений с динамическими колонками.
- Внешние обработки: Выносить сложную логику в отдельные обработки, открываемые по кнопке.
- Хранилище значений: Для временных данных использовать
ХранилищеЗначениявместо реквизитов формы.
Для форм с более чем 50 динамическими элементами рассмотрите возможность создания специализированного справочника настроек, где пользователь будет настраивать отображаемые поля, а форма будет читать эти настройки при открытии.
⚠️ Внимание: В версиях платформы 1С:Предприятие ниже 8.3.18 динамические реквизиты могли вызывать утечки памяти при частом добавлении/удалении. В актуальных версиях проблема решена, но для старых релизов рекомендуется ограничивать количество динамических изменений или перезагружать форму периодически.
FAQ: Частые вопросы по динамическим реквизитам
Можно ли добавить реквизит в форму после её открытия?
Нет, структура формы (реквизиты и элементы) может изменяться только до её отображения. Попытка добавить реквизит после вызова ОткрытьФорму() приведёт к ошибке. Однако можно изменять значения существующих реквизитов и свойства элементов (видимость, доступность и т.д.) в любой момент.
Как сделать динамический реквизит обязательным для заполнения?
Для этого нужно:
- Установить свойство
Обязательное = Истинау объектаРеквизитФормыпри добавлении. - Добавить проверку в обработчике
ПередЗаписью:
Если НЕ ЗначениеЗаполнено(Форма.НовыйРеквизит) Тогда
Предупреждение("Заполните поле 'Новый реквизит'!", 60);
Отказ = Истина;
КонецЕсли;
Можно ли добавить реквизит типа "Табличная часть документа"?
Нет, напрямую добавить реквизит типа "Табличная часть" нельзя, так как это сложный объект, привязанный к метаданным. Альтернативные варианты:
- Использовать
ТаблицаЗначенийс аналогичной структурой. - Добавлять строки в существующую табличную часть документа программно.
- Создать расширение конфигурации с новой табличной частью.
Как сохранить динамические реквизиты в базу данных?
Есть несколько способов:
- Добавить реквизиты в объект: Заранее добавить все возможные реквизиты в метаданные объекта (документа/справочника) и синхронизировать их с формой.
- Использовать хранилище значений:
Объект.ДополнительныеДанные = Новый ХранилищеЗначения(Форма.ДинамическиеДанные); - Создать регистр сведений для хранения произвольных данных, привязанных к объекту.
Наиболее гибкий вариант — хранилище значений, но он не позволяет искать/фильтровать данные по динамическим полям.
Почему динамически добавленный элемент не отображается на форме?
Частые причины:
- Не указано свойство
ИмяДанныху элемента. - Элемент добавлен в невидимый контейнер или за пределами видимой области.
- Координаты элемента (
Позиция) заданы неверно (например,Left = -1000). - Элемент добавлен после отображения формы (нужно использовать
ОбновитьФорму()). - Свойство
Видимостьустановлено вЛожь.
Для диагностики добавьте временный код, который выделяет элемент красным фоном:
НовыйЭлемент.ЦветФона = ВебЦвета.Красный;