При разработке на управляемых формах в платформе 1С:Предприятие 8.3 начинающие разработчики часто сталкиваются с ошибкой: «Ошибка отображения типов: Отсутствует отображение для типа '{http://v8.1c.ru/8.1/data/core}ValueTable'». Эта ошибка обычно возникает при попытке выгрузить результат запроса в объект ТаблицаЗначений, поместить его во временное хранилище и затем вызвать функцию ПолучитьИзВременногоХранилища() непосредственно в клиентской процедуре.
Разберем детально причины такого поведения платформы и рассмотрим правильные варианты передачи табличных данных между контекстами сервера и клиента.
Выясним причину ошибки: тип данных ТаблицаЗначений (а также ДеревоЗначений, РезультатЗапроса и ВыборкаИзРезультатаЗапроса) является сугубо серверным объектом. В контексте тонкого клиента или веб-клиента этот тип отсутствует в целях оптимизации сетевого трафика и безопасности.
Когда мы пытаемся получить из временного хранилища объект серверного типа на клиенте, платформа инициирует механизм XDTO-сериализации (маршалинга). Так как соответствия для типа ValueTable на клиенте нет, платформа выбрасывает исключение времени выполнения.
Обратите также внимание на время жизни временного хранилища: если при вызове метода ПоместитьВоВременноеХранилище(Данные, Новый УникальныйИдентификатор) передается вновь созданный GUID, данные удаляются сервером сразу после завершения серверного вызова. Чтобы данные жили на протяжении всей работы формы, вторым параметром всегда следует указывать ЭтаФорма.УникальныйИдентификатор.
Рассмотрим основной способ работы с табличными данными на форме. На клиенте таблица значений представлена специальным объектом — ДанныеФормыКоллекция (строки имеют тип ДанныеФормыЭлементКоллекции). Этот объект полностью сериализуем и доступен как на сервере, так и на тонком клиенте.
Разберем процесс по шагам:
СписокЗадач) и устанавливаем ему тип ТаблицаЗначений. Добавляем в него нужные колонки: Dat, Регистратор, Customer.ЗначениеВРеквизитФормы.Посмотрим на пример реализации в модуле формы:
&НаСервере
Процедура ЗаполнитьДанныеНаСервере()
// Получаем таблицу значений из серверной функции
ТабЗнач = ОбщийМодульСервер.ПолучитьТаблицуЗадач();
// Заполняем реквизит формы "СписокЗадач" данными из таблицы значений
ЗначениеВРеквизитФормы(ТабЗнач, "СписокЗадач");
КонецПроцедуры
&НаКлиенте
Процедура ПриОткрытии(Отказ)
ЗаполнитьДанныеНаСервере();
// На клиенте реквизит формы уже доступен для прямого обхода
Для Каждого СтрокаЗадачи Из СписокЗадач Цикл
// Обращаемся к колонкам напрямую через точку
Сообщить("Дата: " + Строка(СтрокаЗадачи.Dat) + ", Клиент: " + Строка(СтрокаЗадачи.Customer));
КонецЦикла;
КонецПроцедуры
Для обратного преобразования данных из реквизита формы в объект ТаблицаЗначений на сервере используется парная функция: ТаблицаОбъект = РеквизитФормыВЗначение("СписокЗадач");.
Если табличные данные нужны на клиенте только для временной обработки (например, для настройки оформления элементов, раскраски календаря или вывода сообщений) и их не требуется привязывать к элементам интерфейса, создавать реквизит формы не обязательно.
В этом случае эффективнее сформировать на сервере Массив, элементами которого будут объекты типа Структура. Оба эти типа поддерживаются тонким клиентом.
Рассмотрим пример серверной функции и ее вызова на клиенте:
// В общем модуле (с признаком "Сервер"):
Функция ПолучитьСписокСобытий() Экспорт
СписокДанных = Новый Массив;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Задача.Ссылка КАК Регистратор,
| Задача.Customer КАК Customer,
| Задача.Date КАК ДатаСобытия
|ИЗ
| Задача.Notes КАК Задача
|ГДЕ
| Задача.ПометкаУдаления = ЛОЖЬ";
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
Стр = Новый Структура;
Стр.Вставить("Dat", Выборка.ДатаСобытия);
Стр.Вставить("Регистратор", Выборка.Регистратор);
Стр.Вставить("Customer", Выборка.Customer);
СписокДанных.Добавить(Стр);
КонецЦикла;
Возврат СписокДанных;
КонецФункции
// В модуле формы на клиенте:
&НаКлиенте
Процедура ОбработатьСобытияНаКлиенте()
// Получаем массив структур напрямую с сервера
МассивСобытий = ОбщийМодульСервер.ПолучитьСписокСобытий();
Для Каждого Элемент Из МассивСобытий Цикл
// Элемент является Структурой, доступной на клиенте
Если ЗначениеЗаполнено(Элемент.Dat) Тогда
Элементы.Календарь.ВыделенныеДаты.Добавить(Элемент.Dat);
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Проанализируем частую ошибку при оформлении элементов управления, таких как поле календаря или планировщик. Обработчик события ПриВыводеПериода выполняется исключительно на стороне клиента и вызывается платформой очень часто: при скроллинге, перерисовке окна или смене масштаба.
Важное правило: ни в коем случае нельзя вызывать серверные процедуры (даже &НаСервереБезКонтекста) внутри обработчика ПриВыводеПериода. Это приведет к зависанию интерфейса и множественным лишним сетевым вызовам.
Для максимального быстродействия данные для раскраски следует подготовить заранее (например, при открытии формы) и сохранить в коллекцию Соответствие, где ключом будет выступать чистая дата (начало дня). Поиск в соответствии выполняется за постоянное время O(1), в отличие от медленного перебора массивов и таблиц в цикле.
Посмотрим на правильную реализацию оформления календаря:
&НаКлиенте
Перем СоответствиеЦветовДаты;
&НаКлиенте
Процедура ПриОткрытии(Отказ)
// Инициализируем и заполняем коллекцию один раз при открытии
СоответствиеЦветовДаты = Новый Соответствие;
МассивДат = ПолучитьДатыДляВыделенияНаСервере();
Для Каждого ТекДата Из МассивДат Цикл
СоответствиеЦветовДаты.Вставить(НачалоДня(ТекДата), Истина);
КонецЦикла;
КонецПроцедуры
&НаКлиенте
Процедура ПолеКалендаряПриВыводеПериода(Элемент, ОформлениеПериода)
Если СоответствиеЦветовДаты = Неопределено Тогда
Возврат;
КонецЕсли;
Для Каждого ОформлениеДня Из ОформлениеПериода.Даты Цикл
// Быстрая проверка наличия даты в коллекции без вызовов сервера
Если СоответствиеЦветовДаты[ОформлениеДня.Дата] <> Неопределено Тогда
ОформлениеДня.ЦветТекста = WebЦвета.Бирюзовый;
ОформлениеДня.ЦветФона = WebЦвета.Васильковый;
КонецЕсли;
КонецЦикла;
КонецПроцедуры