Как организовать интерактивную работу с ХранилищемЗначения в управляемых формах 1С

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

При переходе с обычных форм на управляемые разработчики часто сталкиваются с проблемой: типы данных, которые раньше были доступны «всегда и везде», теперь жестко разделены между клиентом и сервером. Одним из таких «камней преткновения» является объект ХранилищеЗначения. В этой статье мы подробно разберем, почему попытка работать с ним напрямую в табличной части приводит к падению производительности, и как реализовать удобный интерактивный интерфейс для редактирования табличных документов, хранящихся в этом объекте.

В чем заключается основная сложность

Проанализируем ситуацию: у нас есть документ с табличной частью, где в одной из колонок хранится ХранилищеЗначения. В нем упакован ТабличныйДокумент. Наша задача — при активации строки таблицы показывать этот документ в поле на форме, позволять пользователю вносить изменения и сохранять их обратно.

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

Разберем стандартную ошибку «Запись при каждом шаге»

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


&НаСервере
Процедура СохранитьТабличныйДокументСервер()
    // Извлекаем весь объект из реквизитов формы (тяжелая операция!)
    ДокументОбъект = РеквизитФормыВЗначение("Объект");
    
    // Создаем хранилище
    Хранилище = Новый ХранилищеЗначения(ТабличныйДокумент);
    
    // Записываем в ТЧ
    ДокументОбъект.ТЧ[ИндексСтроки].Требования = Хранилище;
    
    // Сохраняем в базу (очень тяжелая операция!)
    ДокументОбъект.Записать();
КонецПроцедуры

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

Решение 1: Использование временного хранилища и адресов

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

Рассмотрим алгоритм действий по шагам:

  1. Добавим в табличную часть на форме (не в объекте!) скрытую колонку АдресДанных (тип Строка).
  2. При открытии формы или при первом обращении к строке будем извлекать данные из ХранилищеЗначения и помещать их во временное хранилище.
  3. В колонку АдресДанных запишем полученный адрес.
  4. При активации строки будем просто считывать данные по адресу.

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


&НаСервере
Процедура ПолучитьДанныеДляСтроки(ИдентификаторСтроки)
    
    СтрокаТЧ = Объект.ТЧ.НайтиПоИдентификатору(ИдентификаторСтроки);
    
    // Проверяем, не загружали ли мы данные ранее
    Если ПустаяСтрока(СтрокаТЧ.АдресДанных) Тогда
        // Получаем ТабДок из реквизита объекта (который еще не в форме, а в данных)
        ТабДок = СтрокаТЧ.Требования.Получить();
        Если ТабДок = Неопределено Тогда
            ТабДок = Новый ТабличныйДокумент;
        КонецЕсли;
        
        // Помещаем во временное хранилище с привязкой к форме
        СтрокаТЧ.АдресДанных = ПоместитьВоВременноеХранилище(ТабДок, УникальныйИдентификатор);
    КонецЕсли;
    
    // Загружаем в поле на форме
    ЗначениеИзХранилища = ПолучитьИзВременногоХранилища(СтрокаТЧ.АдресДанных);
    // ПолеТабДока — это реквизит формы с типом ТабличныйДокумент
    ПолеТабДока.Вывести(ЗначениеИзХранилища);
    
КонецПроцедуры

Решение 2: Кэширование на клиенте через Соответствие

Если документов много и они «легкие», мы можем применить кэширование прямо в оперативной памяти клиента. Для этого проанализируем использование объекта Соответствие.

Мы можем создать переменную модуля формы КэшТабДоков. Ключом будет идентификатор строки, а значением — сам объект ТабличныйДокумент. Поскольку ТабличныйДокумент доступен и на сервере, и на клиенте, он прекрасно «летает» между контекстами без упаковки в ХранилищеЗначения.

Важный нюанс: Не делайте вызов сервера напрямую в событии ПриАктивизацииСтроки. Пользователь может быстро листать список, и форма будет «замирать». Используем обработчик ожидания.


&НаКлиенте
Процедура ТЧПриАктивизацииСтроки(Элемент)
    // Отключаем предыдущий вызов, если он был
    ОтключитьОбработчикОжидания("Подключаемый_ЗагрузитьДанныеСтроки");
    // Запускаем небольшую паузу
    ПодключитьОбработчикОжидания("Подключаемый_ЗагрузитьДанныеСтроки", 0.3, Истина);
КонецПроцедуры

&НаКлиенте
Процедура Подключаемый_ЗагрузитьДанныеСтроки()
    ТекущиеДанные = Элементы.ТЧ.ТекущиеДанные;
    Если ТекущиеДанные = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    // Проверяем кэш
    Если КэшТабДоков.Свойство(ТекущиеДанные.ПолучитьИдентификатор()) Тогда
        ПолеТабДока = КэшТабДоков[ТекущиеДанные.ПолучитьИдентификатор()];
    Иначе
        // Идем на сервер только если в кэше нет
        ЗагрузитьДанныеССервера(ТекущиеДанные.ПолучитьИдентификатор());
    КонецЕсли;
КонецПроцедуры

Как правильно сохранить изменения

Когда пользователь отредактировал ПолеТабДока, нам нужно вернуть его в объект, но опять же — без немедленной записи в БД. Мы должны обновить ВременноеХранилище или наш клиентский кэш.

Финальное сохранение произойдет в системном событии ПередЗаписьюНаСервере. Мы проанализируем ситуацию: только в этот момент мы переберем строки ТЧ, возьмем данные из временных адресов или кэша и упакуем их обратно в ХранилищеЗначения реквизита объекта.


&НаСервере
Процедура ПередЗаписьюНаСервере(Отказ, ПараметрыЗаписи)
    Для Каждого СтрокаТЧ Из Объект.ТЧ Цикл
        Если ЭтоАдресВременногоХранилища(СтрокаТЧ.АдресДанных) Тогда
            Данные = ПолучитьИзВременногоХранилища(СтрокаТЧ.АдресДанных);
            // Вот здесь происходит магия упаковки
            СтрокаТЧ.Требования = Новый ХранилищеЗначения(Данные);
        КонецЕсли;
    КонецЦикла;
КонецПроцедуры

Оптимизация производительности: ДанныеФормыВЗначение

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

Совет: Используйте точечные методы. Если вам нужно только прочитать данные одной строки, лучше воспользоваться запросом или методом Объект.ТЧ.НайтиПоИдентификатору() напрямую в контексте серверной процедуры формы, не превращая всю форму в ДокументОбъект.

Подведем итоги

Работа с ХранилищемЗначения в управляемом приложении требует четкого понимания жизненного цикла данных. Помните следующие правила:

  1. Хранилище только для базы: Используйте ХранилищеЗначения только для физического хранения в БД. Внутри формы работайте с «живыми» объектами (ТабличныйДокумент, Картинка) или их адресами.
  2. Временное хранилище — ваш лучший друг: Оно позволяет хранить тяжелые данные в рамках сеанса работы с формой без нагрузки на транзакционный журнал СУБД.
  3. Минимизируйте контекст: Старайтесь передавать на сервер только идентификатор строки, а не весь объект целиком.
  4. Интерфейсный комфорт: Используйте обработчики ожидания (ПодключитьОбработчикОжидания), чтобы перемещение по списку оставалось плавным даже при наличии серверных вызовов.

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

← На главную