Почему нельзя использовать ЗначениеВРеквизитФормы для заполнения табличной части типа Объект.Тч и как это исправить?

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

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

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


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

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

Выясняем причину: почему РеквизитФормыВЗначение("Объект.Тч") работает, а обратная функция — нет?

Для того чтобы понять логику платформы, проанализируем устройство реквизитов управляемой формы. На вкладке «Реквизиты» в редакторе формы объявляются корневые элементы. Например, реквизит Объект (тип ДанныеФормыСтруктура) является полноценным самостоятельным реквизитом формы.

Табличная часть Тч, находящаяся внутри реквизита Объект, является подчиненной коллекцией (тип ДанныеФормыКоллекция). Она не объявлена на верхнем уровне структуры формы как независимый реквизит.

Рассмотрим разницу в поведении двух методов:

  1. РеквизитФормыВЗначение(ИмяРеквизита) — этот метод умеет разбирать составные пути (через точку), такие как "Объект.Тч". Его цель — спуститься по иерархии данных формы до указанной коллекции и преобразовать её в соответствующий прикладной объект (в данном случае в ТаблицуЗначений).
  2. ЗначениеВРеквизитФормы(Значение, ИмяРеквизита) — этот метод устроен иначе. Вторым параметром он строго ожидает имя корневого реквизита формы, объявленного в дереве реквизитов. Платформа 1С не поддерживает частичную перезапись структуры по составному пути через этот метод. Так как "Объект.Тч" не является корневым реквизитом, система выдает ошибку.

Теперь рассмотрим правильные и эффективные способы решения этой задачи.

Решение 1: Использование метода Загрузить() — самый простой и быстрый способ

Если перед нами стоит задача заполнить табличную часть формы из таблицы значений, полученной или обработанной на сервере, наиболее логичным и лаконичным решением будет использование встроенного метода коллекций данных формы Загрузить(). Нам вообще не требуется использовать методы конвертации всей формы.

Рассмотрим пример кода:


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

Метод Загрузить() автоматически сопоставляет колонки таблицы значений с колонками табличной части формы по их именам, очищает старые строки и заполняет коллекцию новыми данными. Это наиболее производительный метод.

Решение 2: Использование глобального метода ЗначениеВДанныеФормы

В платформе 1С существуют две группы методов преобразования. Первые привязаны к контексту управляемой формы (это методы объекта формы РеквизитФормыВЗначение и ЗначениеВРеквизитФормы). Вторые — это глобальные методы, которые доступны в любом серверном контексте и работают напрямую с объектами типов данных формы.

Глобальный метод ЗначениеВДанныеФормы(<Значение>, <ДанныеФормы>) принимает в качестве второго параметра не строковое имя реквизита, а сам объект данных формы, в который требуется поместить результат. Это позволяет обойти ограничение на работу с подчиненными реквизитами через точку.

Посмотрим на пример его использования:


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

Этот код отработает без ошибок, так как метод напрямую сериализует прикладной объект в существующую коллекцию управляемой формы.

Решение 3: Прямой обход коллекции управляемой формы (без конвертации)

Часто разработчики злоупотребляют конвертацией данных формы в прикладные типы (такие как ТаблицаЗначений или ДокументОбъект) без реальной необходимости. Конвертация — это ресурсоемкая операция, требующая времени процессора на сериализацию и десериализацию данных.

Если задача заключается лишь в том, чтобы обойти строки табличной части и установить значения в определенных колонках, это можно сделать напрямую с коллекцией ДанныеФормыКоллекция (которой и является Объект.Тч):


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

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

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

Одним из важных вопросов при проектировании интерфейсов является минимизация объема передаваемых данных между клиентом и сервером. Обычный вызов процедуры с директивой компиляции &НаСервере передает на сервер и обратно весь контекст формы (все реквизиты, элементы управления и состояние формы), что может замедлить работу приложения на слабых каналах связи.

Для решения этой проблемы применяется неконтекстный вызов &НаСервереБезКонтекста. Но напрямую передать коллекцию формы Объект.Тч в такой метод нельзя — платформа выдаст ошибку.

Проанализируем оптимальную схему реализации этой задачи через передачу легкого Массива Структур:

  1. На клиенте мы собираем минимальные параметры для расчета.
  2. Вызываем неконтекстную серверную функцию &НаСервереБезКонтекста. Она выполняет тяжелые запросы или расчеты и возвращает результат в виде массива структур (так как прикладной объект ТаблицаЗначений не может быть передан на сторону клиента напрямую).
  3. На клиенте мы быстро обходим полученный массив и заполняем табличную часть формы.

Посмотрим на практический пример реализации этого паттерна:


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

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

Важное примечание по оптимизации платформы: Согласно стандартам разработки фирмы «1С», не всегда стоит избегать контекстных серверных вызовов. Платформа под капотом оптимизирует передачу данных: при вызове обычного метода &НаСервере передается не вся табличная часть целиком, а только измененные строки (так называемый дифференциальный трафик). Если ваша табличная часть содержит небольшое количество строк (до нескольких сотен), то простой контекстный обход коллекции на сервере (Решение 3) будет более предпочтительным, так как он проще в поддержке и не создает избыточного кода по сериализации данных.

← На главную