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