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