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