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