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