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