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