Как передать таблицу объекта формы в серверный модуль и избежать ошибки изменения данных формы?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке в среде управляемых форм 1С программисты часто выносят бизнес-логику в общие модули. Нередко возникает задача передать табличную часть документа или справочника (реквизита Объект) напрямую из клиентской процедуры в функцию серверного общего модуля. Однако при попытке вызвать метод вида ОбщегоНазначенияСервер.ОбработатьТаблицу(Объект.Товары) платформа прерывает выполнение с ошибкой: «Нельзя изменять поле, содержащее объект данных формы». Разберем детально механизм возникновения этой проблемы и рассмотрим все способы ее решения.

Выясним причину возникновения ошибки

Проанализируем, как платформа 1С:Предприятие 8.3 работает с параметрами методов при клиент-серверном взаимодействии:

  1. Передача параметров по ссылке: По умолчанию в языке 1С параметры процедур и функций передаются по ссылке.
  2. Сериализация данных формы: Табличная часть в контексте формы имеет тип ДанныеФормыКоллекция. При вызове серверного метода с клиента платформа выполняет сериализацию переданной коллекции, отправляет ее на сервер, а по завершении серверного вызова возвращает десериализованные данные обратно на клиент.
  3. Запрет прямого переприсвоения: При возврате управления на клиент платформа пытается обновить переданный аргумент по ссылке, выполняя неявное присвоение в исходное свойство Объект.Товары. В управляемом интерфейсе прямое переприсвоение составных объектов данных формы запрещено архитектурой платформы. Ошибка возникает в момент завершения серверного вызова, даже если в серверном модуле коллекция никак не модифицировалась.

Способ 1. Передача таблицы только для чтения с использованием ключевого слова «Знач»

Если серверный метод должен только прочитать данные таблицы (например, выполнить расчет, проверку заполнения или сформировать сторонний отчет) без внесения изменений в саму форму, самым простым и эффективным решением будет передача параметра по значению.

Укажем в заголовке функции серверного общего модуля ключевое слово Знач:


// Серверный общий модуль (с установленным флагом "Вызов сервера")
Функция ОбработатьТаблицуСервер(Знач ТаблицаТовары) Экспорт

    // ТаблицаТовары доступна для чтения и перебора
    Для Каждого Стр Из ТаблицаТовары Цикл
        // Обработка данных
    КонецЦикла;

    Возврат Истина;
КонецФункции

На стороне формы вызов выполняется напрямую из клиентской процедуры:


&НаКлиенте
Процедура ВыполнитьОбработкуНаКлиенте()
    Результат = гсм_ОбщегоНазначенияСервер.ОбработатьТаблицуСервер(Объект.Товары);
КонецПроцедуры

В этом случае платформа не пытается вернуть измененную ссылку в реквизит формы, и ошибка не возникает.

Способ 2. Использование локальной переменной на клиенте и метода «КопироватьДанныеФормы»

Если требуется передать данные с клиента на сервер, изменить их в серверном общем модуле и вернуть результат обратно в реквизит формы, рассмотрим использование локальной переменной и платформенной процедуры КопироватьДанныеФормы.

Разберем по шагам реализацию данного сценария:

  1. На клиенте присваиваем коллекцию Объект.Товары локальной переменной.
  2. Передаем локальную переменную в серверный метод. Поскольку переменная не связана напрямую со структурой данных формы, платформа может вернуть в нее результат без ошибки.
  3. Применяем процедуру КопироватьДанныеФормы, которая доступна на клиенте и корректно перезаполняет целевую коллекцию формы.

&НаКлиенте
Процедура ИзменитьТаблицуНаКлиенте()
    // 1. Создаем локальную клиентскую переменную
    ТаблицаБуфер = Объект.Товары;
    
    // 2. Вызываем серверный метод общего модуля
    гсм_ОбщегоНазначенияСервер.МодифицироватьТаблицу(ТаблицаБуфер);
    
    // 3. Переносим изменения обратно в реквизит формы
    КопироватьДанныеФормы(ТаблицаБуфер, Объект.Товары);
КонецПроцедуры

Способ 3. Вызов через контекстный серверный метод формы (рекомендуемый подход)

Рассмотрим стандартный архитектурный шаблон платформы 1С. Прямой вызов серверного общего модуля с клиента с передачей всей таблицы целиком приводит к передаче большого объема трафика по сети. Оптимальным решением является контекстный серверный вызов внутри модуля формы.

Посмотрим на пример организации кода:


&НаКлиенте
Процедура КомандаОбработать(Команда)
    ОбработатьТаблицуНаСервере();
КонецПроцедуры

&НаСервере
Процедура ОбработатьТаблицуНаСервере()
    // Передаем коллекцию формы напрямую в общий модуль на сервере
    гсм_ОбщегоНазначенияСервер.МодифицироватьТаблицу(Объект.Товары);
КонецПроцедуры

Внутри серверного вызова формы платформа оптимизирует передачу контекста (передаются только измененные данные), а работа с коллекцией формы не вызывает ошибок присвоения.

Различие методов преобразования: «РеквизитФормыВЗначение» и «ДанныеФормыВЗначение»

Если в общем модуле для обработки требуется объектная табличная часть или стандартная ТаблицаЗначений (например, для использования методов Свернуть(), Сортировать() или выполнения запросов), необходимо выполнить преобразование типов. Разберем, какие методы и в каком контексте следует использовать:

  1. РеквизитФормыВЗначение и ЗначениеВРеквизитФормы:

    Эти методы являются методами контекста формы. Они доступны исключительно в модуле самой формы в процедурах с директивой &НаСервере. В общих модулях и неконтекстных процедурах &НаСервереБезКонтекста вызывать их нельзя.

    
    &НаСервере
    Процедура ПреобразоватьВФорме()
        // Получаем прикладной объект из контекста формы
        ДокументОбъект = РеквизитФормыВЗначение("Объект");
        
        // Вызываем процедуру общего модуля, передав реальную табличную часть
        гсм_ОбщегоНазначенияСервер.ОбработатьТабличнуюЧасть(ДокументОбъект.Товары);
        
        // Возвращаем измененный объект в реквизиты формы
        ЗначениеВРеквизитФормы(ДокументОбъект, "Объект");
    КонецПроцедуры
    
  2. ДанныеФормыВЗначение и ЗначениеВДанныеФормы:

    Это глобальные функции платформы. Они доступны на сервере в любых модулях, включая общие модули без контекста формы. Для их корректной работы вторым параметром обязательно указывается целевой тип данных.

    
    // В общем серверном модуле
    Процедура ОбработатьДанныеФормы(ДанныеФормы) Экспорт
        // Преобразуем ДанныеФормыКоллекция в реальную ТаблицуЗначений
        Таблица = ДанныеФормыВЗначение(ДанныеФормы, Тип("ТаблицаЗначений"));
        
        // Выполняем операции, доступные только для ТаблицыЗначений
        Таблица.Свернуть("Номенклатура", "Количество, Сумма");
        
        // Загружаем результат обратно в переданную коллекцию формы
        ЗначениеВДанныеФормы(Таблица, ДанныеФормы);
    КонецПроцедуры
    

Важные настройки общего модуля и производительность

При реализации вызовов учитываем следующие системные требования:

  1. Флаг «Вызов сервера»: Для возможности прямого обращения к процедурам и функциям общего модуля из клиентского кода формы (с директивой &НаКлиенте) в свойствах общего модуля обязательно должен быть установлен флаг Вызов сервера (Server Call).
  2. Оптимизация сетевого трафика: При прямом вызове общего модуля с клиента вся коллекция Объект.Товары передается по сети целиком дважды (на сервер и обратно). Если табличная часть содержит сотни или тысячи строк, предпочтительно использовать контекстный вызов формы &НаСервере, где платформа самостоятельно отслеживает и передает только измененные данные.
← На главную