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