Как записать данные в объект на управляемой форме 1С: разбор ошибок и лучшие практики

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

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

В чем суть проблемы: Данные формы vs Объект базы данных

Для того чтобы понять причину «исчезновения» данных, проанализируем архитектуру управляемых форм. На клиенте и сервере управляемой формы мы работаем не с реальным объектом базы данных (например, не с ДокументОбъект.ИмяДокумента), а с его облегченной проекцией — типом ДанныеФормыСтруктура (в коде это реквизит Объект).

Когда в коде вызывается метод:

ДокОбъект = РеквизитФормыВЗначение("Объект");

Платформа берет временные данные из формы и создает на их основе полноценный прикладной объект в оперативной памяти сервера. Этот объект — абсолютно независимая копия. Любые изменения, внесенные в ДокОбъект (например, заполнение ширины и высоты полотна), происходят внутри этой копии в памяти сервера. Сама форма и ее реквизит Объект ничего об этих изменениях не знают. Именно поэтому в отладчике внутри процедуры данные видны, а на клиенте в пользовательском интерфейсе поля остаются пустыми.


Решение №1: Использование парного метода обратной конвертации

Если мы создали прикладной объект из реквизита формы с помощью метода РеквизитФормыВЗначение(), то после изменения его свойств мы обязаны вернуть измененные данные обратно в контекст формы. Для этого служит парная процедура ЗначениеВРеквизитФормы(). Она сериализует прикладной объект обратно в структуру данных формы.

Рассмотрим, как должен выглядеть правильный код при таком подходе:


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

Важный нюанс: Этот способ рабочий, но он является избыточным и непроизводительным, если вам нужно просто заполнить несколько простых полей на форме.


Решение №2: Изменение реквизитов напрямую через Объект (Рекомендуемый способ)

Зададимся вопросом: зачем нагружать сервер операциями взаимной конвертации, если реквизит Объект доступен для изменения на сервере напрямую? На сервере Объект имеет тип ДанныеФормыСтруктура, свойства которого можно перезаписывать без какого-либо преобразования.

Разберем максимально простой, быстрый и правильный вариант решения задачи:


&НаСервере 
Процедура ДалееНачалоНаСервере() 
    
    // Изменяем реквизиты напрямую в контексте формы на сервере
    Объект.ШиринаПолотна = Объект.База.Ширина; 
    Объект.ВысотаПолотна = Объект.База.Высота; 
    
КонецПроцедуры

При таком подходе данные автоматически обновятся на клиенте сразу после возврата управления из серверного вызова. Никаких дополнительных вызовов обновления интерфейса делать не требуется.

Когда действительно нужны методы РеквизитФормыВЗначение и ЗначениеВРеквизитФормы?

Использовать тяжелую конвертацию данных формы в полноценный объект базы данных следует только в строго определенных сценариях:

  1. Когда необходимо вызвать экспортный метод, описанный в модуле объекта (например, процедуру расчета спецификации или автоматического заполнения табличной части).
  2. Когда требуется выполнить стандартные методы объекта, которые недоступны у структуры данных формы (например, ПроверитьЗаполнение() или специфические проверки перед записью).
  3. Когда производится непосредственная программная запись объекта в базу данных на сервере (хотя в контексте интерактивной работы с формой это требуется редко).

Критическая ошибка: вызов метода Объект.Записать() на клиенте

В исходном коде автора темы присутствовала строка Объект.Записать();, вызванная в процедуре &НаКлиенте. Это грубейшая ошибка, которая приводит к аварийному завершению работы интерфейса с ошибкой «Метод объекта не обнаружен (Записать)».

Выясним причину: на стороне клиента переменная Объект — это всего лишь программная структура для отображения данных на экране. У нее нет и не может быть метода сохранения данных в СУБД.

Чтобы правильно записать данные формы на клиенте, необходимо использовать метод самой формы:


&НаКлиенте
Процедура ДалееНачало(Команда)
    
    // Выполняем расчеты на сервере
    ДалееНачалоНаСервере();
    
    // Правильный вызов записи данных всей формы в базу данных
    ЭтаФорма.Записать(); 
    
КонецПроцедуры

Метод ЭтаФорма.Записать() (или просто Записать()) запускает стандартное системное событие записи, проверяет заполнение обязательных реквизитов, передает данные на сервер, открывает транзакцию и сохраняет объект в базу данных.


Современный паттерн оптимизации: Работа без контекста формы

Для повышения скорости работы интерфейса (особенно в распределенных сетях или при работе через веб-клиент) рекомендуется минимизировать передачу контекста формы между клиентом и сервером. Если бы нам все же потребовалось преобразовать данные формы в объект для сложных вычислений, идеальным решением было бы использование бесконтекстного вызова &НаСервереБезКонтекста.

Проанализируем пример реализации бесконтекстного изменения данных:


&НаКлиенте
Процедура ДалееНачало(Команда)
    
    // Передаем структуру данных формы в бесконтекстную функцию
    ИзмененныеДанные = ПолучитьЗаполненныйОбъектНаСервере(Объект);
    
    // Копируем измененные свойства обратно в реквизит формы
    ЗаполнитьЗначенияСвойств(Объект, ИзмененныеДанные);
    
КонецПроцедуры

&НаСервереБезКонтекста
Функция ПолучитьЗаполненныйОбъектНаСервере(Знач ДанныеФормы)
    
    // Преобразуем переданные чистые данные в прикладной объект
    ДокОбъект = ДанныеФормыВЗначение(ДанныеФормы, Тип("ДокументОбъект.ИмяВашегоДокумента"));
    
    // Выполняем логику заполнения
    ДокОбъект.ШиринаПолотна = ДокОбъект.База.Ширина;
    ДокОбъект.ВысотаПолотна = ДокОбъект.База.Высота;
    
    // Преобразуем объект обратно в структуру данных формы для возврата
    ЗначениеВДанныеФормы(ДокОбъект, ДанныеФормы);
    
    Возврат ДанныеФормы;
    
КонецФункции

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

← На главную