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

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

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

В этой статье мы подробно, шаг за шагом, разберем, как программно создать реквизит формы с типом ТаблицаЗначений, наполнить его нужным количеством колонок, вывести полученную структуру на форму в виде таблицы и организовать заполнение данными.

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

---

Шаг 1. Подготовка и очистка старых элементов формы

Если наша динамическая таблица создается не один раз при открытии формы, а перестраивается в процессе работы (например, по нажатию кнопки «Обновить» или при изменении параметров отбора), нам обязательно нужно удалять старую структуру перед генерацией новой. Если этого не сделать, платформа выдаст ошибку неуникальности имен элементов или реквизитов.

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

Проанализируем следующий фрагмент кода:


// Имя нашего динамического реквизита на форме - "ДеревоДанныхТЗ"
// Имя элемента таблицы на форме - "ТаблицаДанныхФормы"

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

// 2. Подготавливаем массив путей для удаления самих реквизитов формы
МассивУдаляемыхРеквизитов = Новый Массив;
СуществующийРеквизит = ПолучитьРеквизиты().Найти("ДеревоДанныхТЗ");

Если СуществующийРеквизит <> Неопределено Тогда
    // Чтобы удалить колонки динамической ТЗ, нужно указать пути к ним в формате "ИмяТЗ.ИмяКолонки"
    Для Каждого Колонка Из РеквизитФормыВЗначение("ДеревоДанныхТЗ").Колонки Цикл
        МассивУдаляемыхРеквизитов.Добавить("ДеревоДанныхТЗ." + Колонка.Имя);
    КонецЦикла;
    
    // Добавляем саму таблицу в список на удаление
    МассивУдаляемыхРеквизитов.Добавить("ДеревоДанныхТЗ");
КонецЕсли;

---

Шаг 2. Программное создание реквизитов управляемой формы

Теперь, когда контекст формы очищен от старых объектов, мы можем приступить к созданию новой структуры данных. Для этого используется объект РеквизитФормы и метод формы ИзменитьРеквизиты().

Обратите внимание на важную деталь: добавление самой таблицы и добавление её колонок должно происходить в определенной последовательности, но в рамках одного вызова метода ИзменитьРеквизиты(). Это существенно повышает производительность системы, так как избавляет платформу от необходимости многократно перестраивать контекст формы на сервере.

Давайте разберем пример создания реквизита таблицы и динамического добавления колонок на основе некоторого входящего списка:


МассивДобавляемыхРеквизитов = Новый Массив;

// Добавляем описание самого реквизита таблицы значений
НоваяТЗРеквизит = Новый РеквизитФормы(
    "ДеревоДанныхТЗ",                      // Имя реквизита
    Новый ОписаниеТипов("ТаблицаЗначений"), // Тип данных
    "",                                    // Путь к родительскому объекту (пусто, т.к. это корень формы)
    "Динамическая таблица"                 // Заголовок реквизита
);
МассивДобавляемыхРеквизитов.Добавить(НоваяТЗРеквизит);

// Допустим, у нас есть входящий массив колонок, которые нужно создать
// Каждая колонка описывается структурой: Имя, ТипЗначения, Заголовок
СписокКолонок = Новый Массив;
СписокКолонок.Добавить(Новый Структура("Имя, ТипЗначения, Заголовок", "Номенклатура", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"), "Товар"));
СписокКолонок.Добавить(Новый Структура("Имя, ТипЗначения, Заголовок", "Количество", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 3)), "Кол-во"));
СписокКолонок.Добавить(Новый Структура("Имя, ТипЗначения, Заголовок", "Цена", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2)), "Цена"));

// Перебираем наш список и добавляем колонки как подчиненные реквизиты к нашей ТЗ
Для Каждого ОписаниеКолонки Из СписокКолонок Цикл
    
    НоваяКолонка = Новый РеквизитФормы(
        ОписаниеКолонки.Имя,         // Имя колонки
        ОписаниеКолонки.ТипЗначения,  // Тип данных колонки
        "ДеревоДанныхТЗ",            // ПУТЬ к родителю! Указываем имя создаваемой ТЗ
        ОписаниеКолонки.Заголовок    // Заголовок
    );
    МассивДобавляемыхРеквизитов.Добавить(НоваяКолонка);
    
КонецЦикла;

// Выполняем транзакцию изменения структуры реквизитов формы.
// Передаем как массив добавляемых, так и массив удаляемых реквизитов (подготовленный на Шаге 1)
ИзменитьРеквизиты(МассивДобавляемыхРеквизитов, МассивУдаляемыхРеквизитов);

---

Шаг 3. Создание визуальных элементов управления на форме

Наши реквизиты успешно созданы в памяти сервера, но пользователь их пока не видит. Теперь нам необходимо спроектировать интерфейсную часть — добавить элемент типа ТаблицаФормы и вложить в него подчиненные поля ввода ПолеФормы.

Выполним это с помощью коллекции Элементы:


// Добавляем элемент управления - Таблицу
ТаблицаФормы = Элементы.Добавить("ТаблицаДанныхФормы", Тип("ТаблицаФормы"), Форма.Элементы.ГруппаДляТаблицы);
ТаблицаФормы.ПутьКДанным = "ДеревоДанныхТЗ";
ТаблицаФормы.Отображение = ОтображениеТаблицы.Список;

// Настраиваем поведение таблицы при необходимости
ТаблицаФормы.ИзменятьСоставСтрок = Истина;
ТаблицаФормы.ИзменятьПорядокСтрок = Истина;

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

---

Шаг 4. Заполнение данными и обратное чтение

После создания структуры у разработчиков часто возникает вопрос: как наполнить эту таблицу данными и как потом прочитать то, что ввел пользователь? Обращаться к динамическому реквизиту напрямую как к физической таблице значений нельзя. Для обмена данными между прикладными объектами сервера и реквизитами управляемой формы используются специальные методы-сериализаторы.

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


// 1. Получаем реальные данные с помощью запроса
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |    Ссылка КАК Номенклатура,
    |    10 КАК Количество,
    |    1500 КАК Цена
    |ИЗ
    |    Справочник.Номенклатура КАК Номенклатура
    |ГДЕ
    |    НЕ Номенклатура.ЭтоГруппа";

РезультатЗапроса = Запрос.Выполнить().Выгрузить(); // Возвращает физическую Таблицу Значений

// 2. Наполняем наш динамически созданный реквизит данными таблицы значений
ЗначениеВРеквизитФормы(РезультатЗапроса, "ДеревоДанныхТЗ");

Когда пользователь отредактирует данные на экране, и нам потребуется обработать результат на сервере (например, записать в документ или провести расчеты), мы прочитаем таблицу в обратном порядке:


// Извлекаем физическую Таблицу Значений из реквизита формы
ФизическаяТЗ = РеквизитФормыВЗначение("ДеревоДанныхТЗ");

// Теперь нам доступны все стандартные методы таблиц значений
ФизическаяТЗ.Свернуть("Номенклатура", "Количество");

Для Каждого СтрокаТЗ Из ФизическаяТЗ Цикл
    // Выполняем бизнес-логику
    Если СтрокаТЗ.Количество > 100 Тогда
        ОбщегоНазначенияКлиентСервер.СообщитьПользователю("Превышен лимит количества товара: " + СтрокаТЗ.Номенклатура);
    КонецЕсли;
КонецЦикла;

---

Шаг 5. Привязка обработчиков событий (Действий)

Динамическая таблица становится по-настоящему интерактивной только тогда, когда она умеет реагировать на действия пользователя (например, при изменении значения в ячейке или при выборе строки). Для этого используется метод УстановитьДействие().

Важное условие: процедура, которую мы привязываем в качестве обработчика, должна быть физически прописана в модуле формы с соответствующей директивой компиляции (например, &НаКлиенте) и иметь правильный набор принимаемых параметров, предусмотренный синтаксисом платформы.

Посмотрим, как назначить обработчик изменения поля ввода в нашей колонке:


// Назначим обработчик для созданного поля ввода
ИмяЭлементаКолонки = "ТаблицаДанныхФормы_Количество";
ПолеКоличество = Элементы[ИмяЭлементаКолонки];

// Связываем событие "ПриИзменении" с клиентской процедурой формы "Подключаемый_КоличествоПриИзменении"
ПолеКоличество.УстановитьДействие("ПриИзменении", "Подключаемый_КоличествоПриИзменении");

А в самом модуле управляемой формы мы должны заранее подготовить следующий метод:


&НаКлиенте
Процедура Подключаемый_КоличествоПриИзменении(Элемент)
    
    // Получаем текущую строку таблицы
    ТекущаяСтрока = Элементы.ТаблицаДанныхФормы.ТекущиеДанные;
    Если ТекущаяСтрока <> Неопределено Тогда
        // Производим динамический пересчет, например, суммы
        ТекущаяСтрока.Цена = ТекущаяСтрока.Цена * 1.1; // Увеличим цену на 10% для примера
    КонецЕсли;
    
КонецПроцедуры

---

Резюме и рекомендации

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

При использовании данного подхода всегда помните о следующих правилах:

  1. Все манипуляции с изменением состава реквизитов (методы ИзменитьРеквизиты()) и добавлением элементов должны производиться исключительно на сервере.
  2. Для удаления элементов всегда используйте обратный порядок: сначала подчиненные поля колонок, затем сама таблица-родитель, и только потом — вызов удаления реквизитов формы.
  3. Передавайте все изменения в метод ИзменитьРеквизиты() пакетно (одним массивом), чтобы минимизировать нагрузку на серверную часть приложения и избежать мерцания интерфейса у пользователя.
← На главную