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