При разработке управляемых форм часто возникает задача отображения связанных данных по схеме «Мастер–Деталь» (например, в верхней таблице отображается список документов, а в нижней — табличная часть текущего выбранного документа). Если разработчик пытается получить единым запросом шапку и вложенную выборку строк, а затем применить метод Загрузить() к реквизиту формы типа ДанныеФормыКоллекция, возникает проблема: скалярные колонки (дата, номер, контрагент) успешно заполняются, а колонка с вложенной таблицей остается пустой.
Разберем причины такого поведения платформы «1С:Предприятие 8.3», выясним ограничения стандартных методов и рассмотрим рабочие способы решения данной задачи.
Проанализируем механику работы платформы с коллекциями управляемой формы:
ДанныеФормыКоллекция.Загрузить() выполняет прямое сопоставление по именам колонок и переносит только значения простых типов (строки, числа, даты, булево, ссылки). Рекурсивная загрузка вложенных таблиц и коллекций этим методом не предусмотрена.ТаблицаЗначений существует только на сервере и не может являться полноценным типом данных ячейки в объекте ДанныеФормыКоллекция. Вложенные списки на форме должны быть объявлены как подчиненные реквизиты формы (подчиненные коллекции) либо обрабатываться отдельно.Если структура реквизитов формы уже содержит вложенную коллекцию в строке мастер-таблицы, прямое применение ЗаполнитьЗначенияСвойств() ко всей строке не перенесет вложенную таблицу. В этом случае мы обходим строки результата запроса циклом, отдельно инициализируя каждую строку и загружая данные вложенной таблицы, исключая ее из стандартного заполнения свойств.
Посмотрим на пример серверного кода:
РезультатЗапроса = Запрос.Выполнить().Выгрузить();
ТаблицаДокументов.Очистить();
Для Каждого ТекСтрока Из РезультатЗапроса Цикл
НовСтрока = ТаблицаДокументов.Добавить();
// Заполняем простые реквизиты шапки, исключая колонку со вложенной таблицей
ЗаполнитьЗначенияСвойств(НовСтрока, ТекСтрока,, "ТЗТоваров");
// Загружаем подчиненную коллекцию строк текущего документа
НовСтрока.ТЗТоваров.Загрузить(ТекСтрока.ТЗТоваров);
КонецЦикла;
Этот способ решает задачу в лоб, однако при большом объеме строк в документе и длинном списке документов он может вызывать просадку производительности и увеличивать объем формы в памяти.
Вместо формирования вложенных таблиц в запросе мы можем разделить данные на две независимые плоские таблицы и связать их на форме по идентификатору или ссылке.
Ссылка или УникальныйИдентификатор).ТаблицаДокументов и ТаблицаВсехСтрок с помощью быстрого метода .Загрузить().Рассмотрим реализацию заполнения на сервере:
// Выполняем пакетный запрос
ПакетРезультатов = Запрос.ВыполнитьПакет();
// Загружаем шапки документов
ТаблицаДокументов.Загрузить(ПакетРезультатов[0].Выгрузить());
// Загружаем все детальные строки
ТаблицаВсехСтрок.Загрузить(ПакетРезультатов[1].Выгрузить());
В обработчике события ПриАктивизацииСтроки верхней таблицы на клиенте мы просто фильтруем или перезаполняем подчиненную таблицу строк текущего документа без лишних серверных вызовов.
Если документов много, а их табличные части объемные, оптимально не передавать все строки на форму сразу, а запрашивать их только при смене текущей строки в мастер-таблице.
ПриАктивизацииСтроки для таблицы ТаблицаДокументов.ПодключитьОбработчикОжидания с минимальной задержкой (0.1–0.2 секунды).
&НаКлиенте
Процедура ТаблицаДокументовПриАктивизацииСтроки(Элемент)
ПодключитьОбработчикОжидания("ОбновитьПодчиненнуюТаблицу", 0.2, Истина);
КонецПроцедуры
&НаКлиенте
Процедура ОбновитьПодчиненнуюТаблицу()
ТекДанные = Элементы.ТаблицаДокументов.ТекущиеДанные;
Если ТекДанные <> Неопределено Тогда
ЗаполнитьСтрокиДокументаНаСервере(ТекДанные.Ссылка);
КонецЕсли;
КонецПроцедуры
&НаСервере
Процедура ЗаполнитьСтрокиДокументаНаСервере(СсылкаНаДокумент)
ТаблицаТоваров.Загрузить(СсылкаНаДокумент.Товары.Выгрузить());
КонецПроцедуры
Если форма предназначена исключительно для просмотра данных и анализа (без непосредственного редактирования строк «на лету»), наиболее производительным решением будет использование двух элементов типа ДинамическийСписок:
ГДЕ Документ = &ТекущийДокумент).ПриАктивизацииСтроки передаем ссылку текущей строки в параметр подчиненного списка.Этот подход гарантирует минимальный расход оперативной памяти и максимальную скорость открытия и работы формы за счет считывания данных порциями непосредственно средствами СУБД.