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

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

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

Суть проблемы и ошибочный подход

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

Разработчик в редакторе формы разворачивает состав полей реквизита ссылки и пытается добавить туда пользовательскую колонку формы, рассчитывая заполнить ее программно следующим образом:


Для Каждого СтрокаТабЧасти1 Из Объект.ТабличнаяЧасть1 Цикл
    Для Каждого СтрокаТабЧасти2 Из СтрокаТабЧасти1.Реквизит1.ТабличнаяЧасть2 Цикл
        // Попытка присвоить значение добавленной колонке формы
        СтрокаТабЧасти2.СвойРеквизит = "Тест";
    КонецЦикла;
КонецЦикла;

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

Архитектурные причины поведения платформы

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

  1. Разрыв между контекстом формы и ссылочными данными. Коллекция Объект.ТабличнаяЧасть1 на форме представлена специальным типом ДанныеФормыКоллекция. Колонки, добавленные непосредственно в нее на форме, существуют в контексте клиентского сеанса. Однако поле СтрокаТабЧасти1.Реквизит1 возвращает обычную ссылку — СправочникСсылка. Ссылка не хранит состояние открытой формы. При обращении через точку к свойству ТабличнаяЧасть2 платформа обращается напрямую к базе данных и считывает объектную коллекцию типа СправочникТабличнаяЧасть. Эта коллекция строго ограничена составом метаданных конфигуратора и ничего не знает о добавленных на форме колонках.
  2. Отсутствие поддержки вложенных коллекций в модели данных формы. Модель ДанныеФормы не поддерживает иерархическое вложение коллекций внутрь строк (тип ДанныеФормыКоллекция не может быть вложен в строку другой ДанныеФормыКоллекция).
  3. Поведение редактора в разных версиях платформы. В более ранних версиях платформы (например, в ветке 8.2) редактор формы ошибочно позволял вызывать команду добавления реквизита внутри раскрытого дерева реквизитов ссылки. Это было визуальной ошибкой среды разработки: колонка создавалась в интерфейсе дерева, но не имела привязки к реальной модели данных. Начиная с релизов платформы 8.3 (в частности, 8.3.8 и старше), возможность добавления реквизитов внутрь состава ссылочных типов была полностью заблокирована разработчиками платформы как некорректная.

Правильные способы реализации связанных данных

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

Способ 1. Реализация интерфейса Master-Detail через отдельную таблицу формы

Это классический и наиболее производительный паттерн для работы со связанными таблицами на управляемой форме. Разберем его по шагам:

  1. Создадим на форме отдельный реквизит с типом ТаблицаЗначений (назовем его, например, ПодчиненнаяТаблицаФормы).
  2. Добавим в этот реквизит необходимые колонки из метаданных подчиненной таблицы справочника, а также дополнительную пользовательскую колонку СвойРеквизит.
  3. Перетащим реквизит ПодчиненнаяТаблицаФормы в элементы формы в виде элемента «Таблица».
  4. В основной таблице формы (ТабличнаяЧасть1) назначим обработчик события ПриАктивизацииСтроки.
  5. При смене текущей строки в обработчике будем считывать данные справочника и заполнять таблицу формы:

&НаКлиенте
Процедура ТабличнаяЧасть1ПриАктивизацииСтроки(Элемент)
    ТекущиеДанные = Элемент.ТекущиеДанные;
    Если ТекущиеДанные <> Неопределено И ЗначениеЗаполнено(ТекущиеДанные.Реквизит1) Тогда
        ЗаполнитьПодчиненнуюТаблицуНаСервере(ТекущиеДанные.Реквизит1);
    Иначе
        ПодчиненнаяТаблицаФормы.Очистить();
    КонецЕсли;
КонецПроцедуры

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

Способ 2. Использование реквизита типа «ДеревоЗначений»

Если бизнес-логика требует видеть всю структуру строк одновременно в едином визуальном элементе, иерархию следует выводить через отдельный реквизит формы с типом ДеревоЗначений:

  1. Добавим реквизит формы ДеревоДанных с типом ДеревоЗначений, создав в нем все необходимые колонки верхнего и подчиненного уровней.
  2. При открытии формы сформируем дерево: строки основной таблицы помещаются на верхний уровень, а строки табличной части Реквизит1.ТабличнаяЧасть2 добавляются как дочерние строки соответствующих узлов.
  3. В таком дереве колонка СвойРеквизит доступна для чтения и программной записи на всех уровнях иерархии.

Способ 3. Программное добавление колонок в основную таблицу формы

Если дополнительный реквизит требуется добавить не во вложенную ссылку, а непосредственно в строки Объект.ТабличнаяЧасть1, это можно сделать как интерактивно в редакторе формы, так и программно в событии ПриСозданииНаСервере с помощью метода ИзменитьРеквизиты():


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    МассивРеквизитов = Новый Массив;
    ТипСтрока = Новый ОписаниеТипов("Строка", Новый КвалификаторыСтроки(100));
    
    // Добавляем реквизит в коллекцию формы
    МассивРеквизитов.Добавить(Новый РеквизитФормы("СвойРеквизит", ТипСтрока, "Объект.ТабличнаяЧасть1", "Моя колонка"));
    ИзменитьРеквизиты(МассивРеквизитов);
    
    // Добавляем визуальный элемент на форму
    ЭлементКолонка = Элементы.Добавить("ТабличнаяЧасть1СвойРеквизит", Тип("ПолеФормы"), Элементы.ТабличнаяЧасть1);
    ЭлементКолонка.Вид = ВидПоляФормы.ПолеВвода;
    ЭлементКолонка.ПутьКДанным = "Объект.ТабличнаяЧасть1.СвойРеквизит";
КонецПроцедуры

К реквизитам, созданным на уровне основной коллекции Объект.ТабличнаяЧасть1, обращение в цикле происходит штатно и без ошибок: СтрокаТабЧасти1.СвойРеквизит = "Тест";.

← На главную