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