Работа с управляемыми формами в 1С:Предприятие 8.3 требует гибкости — часто возникает необходимость динамически изменять структуру формы без перезагрузки конфигурации. Один из самых востребованных сценариев — программное добавление реквизита в уже существующую форму. Это может понадобиться при адаптации интерфейса под специфические задачи пользователя, интеграции с внешними системами или реализации сложной бизнес-логики.

В отличие от обычных форм, где реквизиты добавляются статически в конфигураторе, управляемые формы позволяют манипулировать своей структурой "на лету". Однако этот процесс имеет ряд нюансов: от выбора правильного метода (ДобавитьРеквизит() vs ручное создание элементов) до обработки событий и сохранения данных. В этой статье мы разберём все аспекты — от базовых примеров до продвинутых техник с учётом особенностей платформы 8.3.20+, где появились новые возможности работы с динамическими реквизитами.

Почему нельзя просто добавить реквизит в конфигураторе?

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

  • 🔄 Динамические формы — когда состав полей зависит от прав пользователя, настроек системы или данных документа (например, в форме заказа могут появляться дополнительные поля при выборе определённого контрагента).
  • 🔌 Интеграции — при обмене данными с внешними системами, когда структура формы должна адаптироваться под входящие данные (например, JSON с произвольными полями).
  • 📦 Расширения конфигурации — когда нельзя модифицировать исходную конфигурацию, но нужно добавить функциональность.
  • 🧪 Тестирование гипотез — временное добавление реквизитов для проверки новых механизмов без внесения изменений в конфигурацию.

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

📊 Как часто вам приходится динамически изменять формы в 1С?
Постоянно, это часть моей работы
Иногда, для специфических задач
Рядом, но не сам(а)
Никогда, хватает статических форм

Базовый метод: ДобавитьРеквизит()

Самый простой способ добавить реквизит в управляемую форму — использовать метод ДобавитьРеквизит(). Он доступен для объекта УправляемаяФорма и позволяет создать новый реквизит с указанием типа, имени и других параметров. Пример минимального кода:

Процедура ДобавитьНовыйРеквизит(Форма)

Форма.ДобавитьРеквизит("НовыйРеквизит",

Новый ОписаниеТипов("Строка", Новый КвалификаторыСтроки(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. Для элементов ПолеВыбора указать свойство ВыборИзСписка = Истина, если нужно разрешить выбор из справочника.
  3. Обработать случай, когда справочник пуст или недоступен для пользователя.

Убедиться, что справочник существует в конфигурации|Проверить права пользователя на чтение справочника|Обработать случай пустого справочника|Настроить фильтр выбора (если нужен)|Указать форму выбора (по умолчанию или кастомную)-->

Для типов ТаблицаЗначений или ДеревоЗначений процесс усложняется — помимо добавления реквизита нужно динамически формировать структуру колонок. Пример:

// Добавляем реквизит типа "ТаблицаЗначений"

ТаблицаТип = Новый ОписаниеТипов("ТаблицаЗначений");

Форма.ДобавитьРеквизит("ДинамическаяТаблица", ТаблицаТип);

// Настраиваем структуру таблицы

Таблица = Форма.ДинамическаяТаблица;

Таблица.Колонки.Добавить("Наименование", Новый ОписаниеТипов("Строка"));

Таблица.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число"));

Обработка событий и сохранение данных

Динамически добавленные реквизиты участвуют в стандартном жизненном цикле формы, но есть нюансы:

⚠️ Внимание: Если реквизит добавлен программно после вызова события ПриСозданииНаСервере, он не будет сохранён в данных формы при записи. Все динамические реквизиты должны создаваться на этапе инициализации формы.

Для обработки изменений в динамических реквизитах используются стандартные события формы:

Процедура ДинамическаяТаблицаПриИзменении(Элемент)

// Обработка изменения в динамически созданной таблице

Сообщить("Изменена строка: " + Элемент.ТекущиеДанные.Наименование);

КонецПроцедуры

Процедура НовыйРеквизитПриИзменении(Элемент)

// Обработка изменения в динамически добавленном поле

Если НЕ ЗначениеЗаполнено(Элемент.Значение) Тогда

Элемент.Подсказка = "Поле обязательно для заполнения!";

Иначе

Элемент.Подсказка = "";

КонецЕсли;

КонецПроцедуры

Для сохранения данных динамических реквизитов в объект (документ, справочник и т.д.) нужно:

  1. Добавить соответствующие реквизиты в объект до вызова формы (или использовать универсальные реквизиты типа ХранилищеЗначения).
  2. В обработчике ПередЗаписью скопировать значения из формы в объект:
Процедура ПередЗаписью(Отказ, ПараметрыЗаписи)

Объект.ДополнительноеПоле1 = ЗначениеВСтрокуВнутреннее(Форма.НовыйРеквизит);

Объект.ДополнительноеПоле2 = Форма.ДинамическаяТаблица.Выгрузить();

КонецПроцедуры

Альтернативный подход — использование ХранилищеЗначения для произвольных данных:

// При открытии формы

Хранилище = Новый ХранилищеЗначения(Объект.ДополнительныеДанные);

Форма.ДинамическиеДанные = Хранилище.Восстановить();

// При записи

Объект.ДополнительныеДанные = Новый ХранилищеЗначения(Форма.ДинамическиеДанные);

Ошибки и их решения

При работе с динамическими реквизитами разработчики часто сталкиваются с типичными ошибками. Рассмотрим самые распространённые и способы их исправления:

Ошибка Причина Решение
Реквизит с таким именем уже существует Повторное добавление реквизита с тем же именем Проверять существование реквизита через Форма.Реквизиты.Найти(Имя)
Невозможно изменить форму: она уже отображена Попытка добавить реквизит после открытия формы Выносить добавление реквизитов в ПриСозданииНаСервере
Тип не найден: СправочникСсылка.НесуществующийСправочник Ошибка в имени типа или справочник удалён Проверять существование типа через Метаданные.Справочники.Найти()
Элемент не отображается на форме Не указано свойство ИмяДанных или ошибка в позиционировании Проверять связь элемента с реквизитом и координаты размещения
Значение не является значением объекта Попытка записать значение неверного типа в реквизит Контролировать типы данных при присвоении значений

Для отладки динамических форм полезно использовать следующие приёмы:

  • 🐞 Вывод в отладочное окно: Сообщить(Форма.Реквизиты.ВыгрузитьКолонку("Имя"));
  • 🔍 Проверка структуры формы через Форма.ОписаниеОпций()
  • 📸 Скриншот элементов: временно сделать элементы видимыми с ярким фоном для проверки позиционирования.
💡

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

Практические примеры использования

Рассмотрим реальные сценарии, где программное добавление реквизитов оказывается наиболее полезным.

Пример 1: Динамические поля в документе "Заказ клиента"

Задача: в зависимости от выбранного клиента в заказе должны появляться дополнительные поля (например, "Номер договора" для юридических лиц или "Дату рождения" для физических).

Процедура КонтрагентПриИзменении(Элемент)

Если Элемент.Значение.ЭтотОбъект.ЭтоГруппа() Тогда

Возврат;

КонецЕсли;

// Удаляем старые динамические поля, если они были

Если Форма.Реквизиты.Найти("НомерДоговора") <> Неопределено Тогда

Форма.УдалитьРеквизит("НомерДоговора");

Форма.Элементы.Удалить("НомерДоговора_Элемент");

КонецЕсли;

// Добавляем новое поле в зависимости от типа контрагента

Если Элемент.Значение.ЮрФизЛицо = Перечисления.ТипыКонтрагентов.ЮридическоеЛицо Тогда

ДобавитьПолеВвода(Форма, "НомерДоговора", "Номер договора", "");

ИначеЕсли Элемент.Значение.ЮрФизЛицо = Перечисления.ТипыКонтрагентов.ФизическоеЛицо Тогда

ДобавитьПолеВвода(Форма, "ДатаРождения", "Дата рождения", ТекущаяДата());

КонецЕсли;

КонецПроцедуры

Пример 2: Интеграция с внешней системой

Задача: при получении JSON от внешнего сервиса нужно динамически создать поля в форме для отображения дополнительных данных.

Процедура ЗагрузитьДополнительныеДанные(JSONСтрока)

Данные = JSON.Прочитать(JSONСтрока);

Для Каждого Поле Из Данные Цикл

ИмяПоля = "ДопПоле_" + Поле.Ключ;

Если Форма.Реквизиты.Найти(ИмяПоля) = Неопределено Тогда

Форма.ДобавитьРеквизит(ИмяПоля, Новый ОписаниеТипов("Строка"));

НовыйЭлемент = Форма.Элементы.Добавить(ИмяПоля + "_Элемент",

Тип("ПолеВвода"),

Новый ПолеВвода(ИмяПоля));

НовыйЭлемент.Заголовок = Поле.Ключ;

НовыйЭлемент.Значение = Поле.Значение;

Иначе

Форма[ИмяПоля] = Поле.Значение;

КонецЕсли;

КонецЦикла;

КонецПроцедуры

Пример 3: Настройка отчёта пользователем

Задача: позволить пользователю самостоятельно добавлять поля в форму настройки отчёта.

Процедура ДобавитьПользовательскоеПоле(Имя, Тип, Заголовок)

// Добавляем реквизит

ТипЗначения = Новый ОписаниеТипов(Тип);

Форма.ДобавитьРеквизит("ПользПоле_" + Имя, ТипЗначения);

// Добавляем элемент в специальную группу

Группа = Форма.Элементы.ПользовательскиеПоля;

НовыйЭлемент = Форма.Элементы.Добавить("ПользПоле_" + Имя + "_Элемент",

Тип("ПолеВвода"),

Группа);

НовыйЭлемент.Заголовок = Заголовок;

НовыйЭлемент.ИмяДанных = "ПользПоле_" + Имя;

// Сохраняем настройки для будущих открытий формы

НастройкиПользователя = ПолучаемНастройкиПользователя();

НастройкиПользователя.Добавить(Имя, Новый Структура("Тип,Заголовок", Тип, Заголовок));

СохранитьНастройкиПользователя(НастройкиПользователя);

КонецПроцедуры

Оптимизация производительности

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

  • Кэширование структуры: Если форма открывается часто с одинаковым набором динамических полей, кэшируйте их описание (например, в регистре сведений) и восстанавливайте при открытии.
  • 🗑️ Удаление ненужных реквизитов: Всегда удаляйте динамические реквизиты методом УдалитьРеквизит(), если они больше не нужны. Это уменьшает объём данных формы.
  • 🔄 Ленивая загрузка: Добавляйте реквизиты только когда они действительно нужны (например, при активации определённой закладки).
  • 📊 Группировка элементов: Размещайте динамические элементы в скрытых контейнерах и показывайте их только при необходимости.

Для форм с большим количеством динамических элементов (более 20) имеет смысл рассмотреть альтернативные подходы:

  1. Использование таблиц: Вместо множества отдельных полей использовать таблицу значений с динамическими колонками.
  2. Внешние обработки: Выносить сложную логику в отдельные обработки, открываемые по кнопке.
  3. Хранилище значений: Для временных данных использовать ХранилищеЗначения вместо реквизитов формы.
💡

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

⚠️ Внимание: В версиях платформы 1С:Предприятие ниже 8.3.18 динамические реквизиты могли вызывать утечки памяти при частом добавлении/удалении. В актуальных версиях проблема решена, но для старых релизов рекомендуется ограничивать количество динамических изменений или перезагружать форму периодически.

FAQ: Частые вопросы по динамическим реквизитам

Можно ли добавить реквизит в форму после её открытия?

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

Как сделать динамический реквизит обязательным для заполнения?

Для этого нужно:

  1. Установить свойство Обязательное = Истина у объекта РеквизитФормы при добавлении.
  2. Добавить проверку в обработчике ПередЗаписью:
Если НЕ ЗначениеЗаполнено(Форма.НовыйРеквизит) Тогда

Предупреждение("Заполните поле 'Новый реквизит'!", 60);

Отказ = Истина;

КонецЕсли;

Можно ли добавить реквизит типа "Табличная часть документа"?

Нет, напрямую добавить реквизит типа "Табличная часть" нельзя, так как это сложный объект, привязанный к метаданным. Альтернативные варианты:

  • Использовать ТаблицаЗначений с аналогичной структурой.
  • Добавлять строки в существующую табличную часть документа программно.
  • Создать расширение конфигурации с новой табличной частью.
Как сохранить динамические реквизиты в базу данных?

Есть несколько способов:

  1. Добавить реквизиты в объект: Заранее добавить все возможные реквизиты в метаданные объекта (документа/справочника) и синхронизировать их с формой.
  2. Использовать хранилище значений:
    Объект.ДополнительныеДанные = Новый ХранилищеЗначения(Форма.ДинамическиеДанные);
  3. Создать регистр сведений для хранения произвольных данных, привязанных к объекту.

Наиболее гибкий вариант — хранилище значений, но он не позволяет искать/фильтровать данные по динамическим полям.

Почему динамически добавленный элемент не отображается на форме?

Частые причины:

  • Не указано свойство ИмяДанных у элемента.
  • Элемент добавлен в невидимый контейнер или за пределами видимой области.
  • Координаты элемента (Позиция) заданы неверно (например, Left = -1000).
  • Элемент добавлен после отображения формы (нужно использовать ОбновитьФорму()).
  • Свойство Видимость установлено в Ложь.

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

НовыйЭлемент.ЦветФона = ВебЦвета.Красный;