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