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