Передача сформированной в коде таблицы значений на форму и её последующее отображение — одна из стандартных задач при разработке в среде 1С:Предприятие 8.3. Однако при переходе с обычных форм на управляемые разработчики часто сталкиваются с ошибками типизации и клиент-серверного взаимодействия.
Проанализируем ключевые особенности клиент-серверной архитектуры платформы. Объект ТаблицаЗначений существует исключительно на стороне сервера. Его невозможно использовать на тонком или веб-клиенте, а также нельзя напрямую передать в структуру параметров клиентского метода ОткрытьФорму(), так как данный тип не поддерживает сериализацию. Кроме того, реквизит управляемой формы с типом «ТаблицаЗначений» в контексте исполняемого кода формы представляет собой специальный объект — ДанныеФормыКоллекция.
Рассмотрим подробнее типовые сценарии и выясним, как правильно передать данные и вывести их пользователю в зависимости от того, известна ли структура колонок заранее.
Если нам необходимо открыть новую форму и передать в неё уже заполненную таблицу значений, стандартным и наиболее производительным решением является использование временного хранилища. При этом вместо устаревшего метода ПолучитьФорму() следует использовать ОткрытьФорму().
Разберем по шагам реализацию этого механизма:
УникальныйИдентификатор. Это гарантирует, что данные не будут очищены сборщиком мусора до момента их прочтения.ОткрытьФорму().ПриСозданииНаСервере открываемой формы извлекаем таблицу значений и загружаем её в данные формы.Посмотрим на пример кода вызова из формы-источника или общей команды:
&НаСервере
Функция ПоместитьТаблицуВХранилище()
// Создаем или получаем готовую таблицу значений
ТаблицаДанных = Новый ТаблицаЗначений;
ТаблицаДанных.Колонки.Добавить("Артикул", Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(20)));
ТаблицаДанных.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(10, 2)));
СтрокаТаблицы = ТаблицаДанных.Добавить();
СтрокаТаблицы.Артикул = "A-001";
СтрокаТаблицы.Количество = 15;
// Обязательно связываем адрес с УникальнымИдентификатором формы
Возврат ПоместитьВоВременноеХранилище(ТаблицаДанных, УникальныйИдентификатор);
КонецФункции
&НаКлиенте
Процедура ОткрытьФормуСТаблицей()
АдресДанных = ПоместитьТаблицуВХранилище();
ПараметрыОткрытия = Новый Структура;
ПараметрыОткрытия.Вставить("АдресХранилищаТаблицы", АдресДанных);
ОткрытьФорму("Обработка.МояОбработка.Форма.ФормаОтображения", ПараметрыОткрытия);
КонецПроцедуры
Теперь рассмотрим код, который должен располагаться в модуле открываемой формы (где уже создан реквизит формы ТаблицаНаФорме с соответствующими колонками):
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
Если Параметры.Свойство("АдресХранилищаТаблицы") И ЗначениеЗаполнено(Параметры.АдресХранилищаТаблицы) Тогда
ТаблицаИзХранилища = ПолучитьИзВременногоХранилища(Параметры.АдресХранилищаТаблицы);
Если ТипЗнч(ТаблицаИзХранилища) = Тип("ТаблицаЗначений") Тогда
// Загружаем данные в реквизит формы
ЗначениеВРеквизитФормы(ТаблицаИзХранилища, "ТаблицаНаФорме");
// Также доступен альтернативный метод:
// ЭтотОбъект.ТаблицаНаФорме.Загрузить(ТаблицаИзХранилища);
// Очищаем временное хранилище после чтения для экономии памяти
УдалитьИзВременногоХранилища(Параметры.АдресХранилищаТаблицы);
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Если таблица значений формируется непосредственно в контексте открытой формы (например, по нажатию кнопки), передача через временное хранилище не требуется. Достаточно серверного вызова, где таблица значений преобразуется в объект ДанныеФормыКоллекция.
В платформе 1С для этого предусмотрено два способа:
ЗначениеВРеквизитФормы(ТаблицаИсточник, "ИмяРеквизита") — выполняет полное сопоставление типов и переносит структуру в реквизит формы.ЭлементыДанных.Загрузить(ТаблицаИсточник) — заполняет существующие строки реквизита формы по совпадающим именам колонок.Посмотрим на пример работы в модуле открытой формы:
&НаСервере
Процедура ЗаполнитьРеквизитНаСервере()
Таблица = Новый ТаблицаЗначений;
Таблица.Колонки.Добавить("Код", Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(10)));
Таблица.Колонки.Добавить("Наименование", Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(100)));
НоваяСтрока = Таблица.Добавить();
НоваяСтрока.Код = "00001";
НоваяСтрока.Наименование = "Товар 1";
// Способ переноса данных:
ЗначениеВРеквизитФормы(Таблица, "ТаблицаНаФорме");
КонецПроцедуры
&НаКлиенте
Процедура ЗаполнитьТаблицу(Команда)
ЗаполнитьРеквизитНаСервере();
КонецПроцедуры
Проанализируем ситуацию, когда состав колонок исходной таблицы значений заранее не известен (например, при формировании произвольных отчетов или загрузке произвольных файлов). В этом случае в конфигураторе создается пустой реквизит формы с типом ТаблицаЗначений, а колонки и элементы интерфейса добавляются программно во время выполнения.
Важно: метод ИзменитьРеквизиты() приводит к переинициализации контекста формы на сервере, что повышает нагрузку. Если процедура выполняется повторно в процессе работы, необходимо обязательно удалить ранее созданные динамические реквизиты, передав массив их имен вторым параметром метода ИзменитьРеквизиты(), чтобы избежать ошибки дублирования имен.
Разберем подробный программный код динамического построения таблицы:
&НаСервере
Процедура ВывестиТаблицуЗначенияВКодеВТаблицуНаФорме(ТабВКоде, ИмяРеквизитаТаблицы, ИмяЭлементаТаблицы)
// 1. Формируем массив новых реквизитов для колонок
НовыеРеквизиты = Новый Массив;
Для Каждого Колонка Из ТабВКоде.Колонки Цикл
НовыеРеквизиты.Добавить(
Новый РеквизитФормы(
Колонка.Имя,
Колонка.ТипЗначения,
ИмяРеквизитаТаблицы,
Колонка.Заголовок
)
);
КонецЦикла;
// Применяем изменения структуры формы
ИзменитьРеквизиты(НовыеРеквизиты);
// 2. Создаем визуальные элементы формы для каждой колонки
РодительТаблица = Элементы[ИмяЭлементаТаблицы];
Для Каждого Колонка Из ТабВКоде.Колонки Цикл
ИмяЭлементаКолонки = ИмяРеквизитаТаблицы + "_" + Колонка.Имя;
НовыйЭлемент = Элементы.Добавить(ИмяЭлементаКолонки, Тип("ПолеФормы"), РодительТаблица);
НовыйЭлемент.Вид = ВидПоляФормы.ПолеВвода;
НовыйЭлемент.ПутьКДанным = ИмяРеквизитаТаблицы + "." + Колонка.Имя;
НовыйЭлемент.Заголовок = ?(ЗначениеЗаполнено(Колонка.Заголовок), Колонка.Заголовок, Колонка.Имя);
КонецЦикла;
// 3. Заполняем реквизит данными из таблицы
ЗначениеВРеквизитФормы(ТабВКоде, ИмяРеквизитаТаблицы);
КонецПроцедуры
&НаСервере
Процедура СоздатьТаблицуНаФормеНаСервере()
ТабВКоде = Новый ТаблицаЗначений;
ТабВКоде.Колонки.Добавить("Колонка1", Новый ОписаниеТипов("Строка"));
ТабВКоде.Колонки.Добавить("Колонка2", Новый ОписаниеТипов("Число"));
ТабВКоде.Колонки.Добавить("Колонка3", Новый ОписаниеТипов("Дата"));
СтрВКоде = ТабВКоде.Добавить();
СтрВКоде.Колонка1 = "Тестовая строка";
СтрВКоде.Колонка2 = 42;
СтрВКоде.Колонка3 = ТекущаяДата();
ВывестиТаблицуЗначенияВКодеВТаблицуНаФорме(ТабВКоде, "ТабРеквизит", "ТабНаФорме");
КонецПроцедуры
&НаКлиенте
Процедура СоздатьТаблицуНаФорме(Команда)
СоздатьТаблицуНаФормеНаСервере();
КонецПроцедуры
Таким образом, для статически определенных колонок оптимальным является использование временного хранилища и функции ЗначениеВРеквизитФормы(), а при динамической структуре данных применяется связка методов ИзменитьРеквизиты() и Элементы.Добавить().