Как реализовать динамический набор реквизитов на управляемой форме 1С 8.3?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

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

В этой статье мы подробно разберем два принципиально разных подхода к решению этой задачи на платформе 1С:Предприятие 8.3 (Управляемые формы):

  1. Программное изменение состава реквизитов и элементов формы (динамическое добавление «на лету» с последующим удалением).
  2. Использование универсальной таблицы (дерева) значений на форме как более производительной и простой в поддержке альтернативы.

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


Метод 1. Программное создание и удаление реквизитов формы

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

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

  1. Реквизит формы — логическая переменная в памяти сервера, которая хранит значение (создается через метод ИзменитьРеквизиты).
  2. Элемент формы — визуальный элемент управления в интерфейсе (поле ввода, переключатель, флажок), создаваемый в коллекции Элементы.
  3. Связь по данным — привязка визуального элемента к реквизиту формы через свойство элемента ПутьКДанным.

Шаг 1. Программное добавление реквизитов и элементов

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


&НаСервере
Процедура ДобавитьДинамическиеРеквизитыНаСервере(СтруктураПараметров)

    // Шаг 1. Подготавливаем массив новых реквизитов
    МассивНовыхРеквизитов = Новый Массив;
    ПрефиксИмен = "Динам_"; // Используем префикс для последующей идентификации
    
    Для Каждого Параметр Из СтруктураПараметров Цикл
        
        ИмяРеквизита = ПрефиксИмен + Параметр.Ключ;
        ТипРеквизита = Параметр.Значение; // Здесь передается ОписаниеТипов
        
        НовыйРеквизит = Новый РеквизитФормы(ИмяРеквизита, ТипРеквизита, , Параметр.Ключ, Истина);
        МассивНовыхРеквизитов.Добавить(НовыйРеквизит);
        
    КонецЦикла;
    
    // Добавляем реквизиты на форму
    ЭтаФорма.ИзменитьРеквизиты(МассивНовыхРеквизитов);
    
    // Шаг 2. Создаем визуальные элементы на форме
    РодительскаяГруппа = Элементы.ГруппаДинамическиеПоля; // Предварительно созданная группа на форме
    
    Для Каждого Параметр Из СтруктураПараметров Цикл
        
        ИмяРеквизита = ПрефиксИмен + Параметр.Ключ;
        ИмяЭлемента = "Поле_" + ИмяРеквизита;
        
        // Создаем поле ввода
        НовыйЭлемент = Элементы.Добавить(ИмяЭлемента, Тип("ПолеФормы"), РодительскаяГруппа);
        НовыйЭлемент.Вид = ВидПоляФормы.ПолеВвода;
        НовыйЭлемент.Заголовок = Параметр.Ключ;
        
        // Устанавливаем связь с данными
        НовыйЭлемент.ПутьКДанным = ИмяРеквизита;
        
        // При необходимости можно назначить обработчик события "ПриИзменении"
        НовыйЭлемент.УстановитьДействие("ПриИзменении", "Подключаемый_ДинамическоеПолеПриИзменении");
        
    КонецЦикла;

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

Шаг 2. Правильное удаление программных реквизитов

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

Для корректного удаления необходимо соблюдать строгую последовательность: сначала удаляются визуальные элементы, и только затем — сами реквизиты. Для поиска «своих» реквизитов мы используем префикс Динам_.

Ещё один важный нюанс: метод ИзменитьРеквизиты во втором параметре (удаляемые реквизиты) принимает массив строк (путей к реквизитам), а не массив объектов типа РеквизитФормы.


&НаСервере
Процедура УдалитьДинамическиеРеквизитыНаСервере()

    ПрефиксИмен = "Динам_";
    МассивИменДляУдаления = Новый Массив;
    ЭлементыДляУдаления = Новый Массив;
    
    // 1. Собираем имена элементов и реквизитов для удаления
    ВсеРеквизитыФормы = ЭтаФорма.ПолучитьРеквизиты();
    Для Каждого Реквизит Из ВсеРеквизитыФормы Цикл
        
        // Проверяем наличие нашего префикса в имени реквизита
        Если Найти(Реквизит.Имя, ПрефиксИмен) = 1 Тогда
            
            МассивИменДляУдаления.Добавить(Реквизит.Имя);
            
            // Ищем связанный элемент формы
            ИмяЭлемента = "Поле_" + Реквизит.Имя;
            НайденныйЭлемент = Элементы.Найти(ИмяЭлемента);
            Если НайденныйЭлемент <> Неопределено Тогда
                ЭлементыДляУдаления.Добавить(НайденныйЭлемент);
            КонецЕсли;
            
        КонецЕсли;
        
    КонецЦикла;
    
    // 2. Сначала удаляем визуальные элементы
    Для Каждого Элемент Из ЭлементыДляУдаления Цикл
        Элементы.Удалить(Элемент);
    КонецЦикла;
    
    // 3. Только после этого удаляем сами реквизиты из памяти формы
    Если МассивИменДляУдаления.Количество() > 0 Тогда
        ЭтаФорма.ИзменитьРеквизиты(, МассивИменДляУдаления);
    КонецЕсли;

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

Шаг 3. Интеграция в интерактивный процесс (Событие ПриИзменении)

Выясним, как связать эти механизмы с действиями пользователя. Например, при выборе вида изделия в поле ТипИзделия состав параметров должен мгновенно перестраиваться.

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


&НаКлиенте
Процедура ТипИзделияПриИзменении(Элемент)
    
    // Перестраиваем форму на сервере
    ПерестроитьДинамическиеПоляНаСервере(Объект.ТипИзделия);
    
КонецПроцедуры

&НаСервере
Процедура ПерестроитьДинамическиеПоляНаСервере(ВыбранныйТип)

    // Очищаем старые реквизиты
    УдалитьДинамическиеРеквизитыНаСервере();
    
    // Получаем структуру новых параметров (например, из регистра сведений или настроек БСП)
    СтруктураПараметров = ПолучитьСписокПараметровДляТипа(ВыбранныйТип);
    
    // Создаем новые реквизиты
    ДобавитьДинамическиеРеквизитыНаСервере(СтруктураПараметров);

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

Существенный минус данного подхода: Контекстный вызов сервера при изменении полей заставляет платформу передавать все данные формы (состояние, реквизиты, элементы) с клиента на сервер и обратно. Если пользователь быстро переключает типы изделий, интерфейс может субъективно «подтормаживать» из-за сетевых задержек и времени перерисовки формы платформой.


Метод 2. Альтернативный вариант: Универсальная таблица параметров

Рассмотрим более изящное, быстрое и профессиональное решение, которое часто используется в типовых конфигурациях 1С (например, для работы со свойствами и характеристиками). Вместо деструктивного изменения структуры формы при каждом клике, мы создаем в конфигураторе всего один постоянный элемент — таблицу значений (или дерево значений) прямо на форме.

Архитектура решения:

Главная магия заключается в том, что для колонки Значение тип данных может динамически настраиваться для каждой строки индивидуально!

Реализация на сервере при изменении типа изделия:

В этом случае нам не нужно вызывать тяжелые методы ИзменитьРеквизиты. Мы просто очищаем строки таблицы и заполняем их новыми записями с назначением типов данных для ячеек.


&НаСервере
Процедура ЗаполнитьТаблицуПараметровНаСервере(ВыбранныйТип)

    // Очищаем таблицу параметров
    ТаблицаПараметров.Очистить();
    
    // Получаем список нужных параметров для данного типа изделия
    СписокПараметров = ПолучитьСписокПараметровДляТипа(ВыбранныйТип); // Возвращает ТаблицуЗначений с полями Имя, Представление, ТипДанных
    
    Для Каждого СтрокаНастройки Из СписокПараметров Цикл
        
        НоваяСтрока = ТаблицаПараметров.Добавить();
        НоваяСтрока.ИмяПараметра  = СтрокаНастройки.Имя;
        НоваяСтрока.Представление = СтрокаНастройки.Представление;
        
        // Ограничиваем тип ячейки ввода конкретным типом данных для этой строки
        НоваяСтрока.Значение = СтрокаНастройки.ТипДанных.ПривестиЗначение(Неопределено);
        
    КонецЦикла;

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

Динамическое ограничение типов в ячейке ТЧ

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

Проанализируем пример настройки типа поля ввода при активизации строки на клиенте:


&НаКлиенте
Процедура ТаблицаПараметровПриАктивизацииСтроки(Элемент)
    
    ТекущиеДанные = Элементы.ТаблицаПараметров.ТекущиеДанные;
    Если ТекущиеДанные = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    // Получаем описание типов для текущего параметра
    ОписаниеТиповПараметра = ПолучитьОписаниеТиповДляПараметра(ТекущиеДанные.ИмяПараметра);
    
    // Устанавливаем ограничение типов для элемента управления в текущей строке
    Элементы.ТаблицаПараметровЗначение.ОграничениеТипов = ОписаниеТиповПараметра;
    
КонецПроцедуры

Преимущества использования таблицы параметров:

  1. Высокая скорость работы: Очистка и заполнение строк таблицы происходит мгновенно, без перестроения структуры интерфейса и выделения новой памяти под метаданные формы.
  2. Легкость расчетов: Для проведения расчетов в калькуляторе не нужно динамически обращаться к свойствам формы через квадратные скобки. Достаточно обойти строки таблицы в простом цикле:

&НаКлиентеНаСервереБезКонтекста
Функция ВычислитьРезультат(ТаблицаПараметров)
    
    // Собираем параметры в структуру для удобного расчета
    СтруктураПараметров = Новый Структура;
    Для Каждого Строка Из ТаблицаПараметров Цикл
        СтруктураПараметров.Вставить(Строка.ИмяПараметра, Строка.Значение);
    КонецЦикла;
    
    // Пример расчета формулы: Результат = Ширина * Высота * Глубина
    Ширина = 0; Высота = 0; Глубина = 0;
    СтруктураПараметров.Свойство("Ширина", Ширина);
    СтруктураПараметров.Свойство("Высота", Высота);
    СтруктураПараметров.Свойство("Глубина", Глубина);
    
    Возврат Ширина * Высота * Глубина;

ФункцияКонец

Резюме: Какой подход выбрать?

Выбор архитектурного решения напрямую зависит от ваших бизнес-требований к интерфейсу:

← На главную