Отбор динамического списка из кода: компоновщик, параметры формы и БСП
Содержание
Программно установить отбор в динамическом списке 1С 8.3 можно через компоновщик настроек: добавьте ЭлементОтбораКомпоновкиДанных в Список.КомпоновщикНастроек.ФиксированныеНастройки.Отбор в обработчике ПриСозданииНаСервере. Если конфигурация построена на БСП, то же самое делает одна строка: ОбщегоНазначенияКлиентСервер.УстановитьЭлементОтбораДинамическогоСписка(Список, "Поле", Значение).
Типичная ситуация: бухгалтер просит, чтобы в форме подбора договоров сразу показывались только договоры выбранного контрагента и только действующие. Ручной отбор пользователь снимет или забудет поставить. Программный отбор встаёт при открытии формы, а при желании его можно спрятать от пользователя совсем.
Как устроен динамический список и где у него живёт отбор
Динамический список не хранит данные в форме. Он читает их из базы порциями, по мере прокрутки, и каждый раз строит запрос заново. Поэтому отбор здесь — не фильтр уже загруженной таблицы, а условие, которое платформа добавляет в запрос.
Отсюда главное отличие от обычного запроса. В запросе вы пишете ГДЕ Владелец = &Владелец один раз и получаете результат. В динамическом списке условие задаётся через настройки компоновки данных, и платформа сама подставляет его при каждом чтении порции, сортировке и поиске.
У списка несколько уровней настроек, и от выбора уровня зависит, увидит ли пользователь ваш отбор:
| Где задать отбор | Видит пользователь | Может снять | Когда применять |
|---|---|---|---|
КомпоновщикНастроек.ФиксированныеНастройки.Отбор | нет | нет | жёсткое ограничение по смыслу формы |
КомпоновщикНастроек.Настройки.Отбор | да, в настройке списка | да | отбор «по умолчанию», который можно поменять |
Параметр формы Отбор | нет | нет | отбор задаёт вызывающий код при открытии |
Параметр запроса &Имя | нет | нет | условие внутри произвольного запроса |
Свойство Список.Отбор у динамического списка тоже есть, его часто встречают в старом коде. В новом коде лучше обращаться к компоновщику явно: так сразу понятно, на каком уровне стоит условие.
Отбор работает только по полям, которые есть в списке. Для списка на основной таблице это её реквизиты, для произвольного запроса — поля из секции ВЫБРАТЬ. Поле можно не выводить в колонки таблицы, но в запросе оно быть должно.
Отбор при открытии формы: ПриСозданииНаСервере и параметр «Отбор»
Способ 1. Код в модуле формы списка
Самый частый вариант. Пишем в форме, где лежит реквизит Список типа ДинамическийСписок:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
Если Не ЗначениеЗаполнено(Параметры.Контрагент) Тогда
Возврат;
КонецЕсли;
ЭлементОтбора = Список.КомпоновщикНастроек.ФиксированныеНастройки.Отбор.Элементы.Добавить(
Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Владелец");
ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
ЭлементОтбора.ПравоеЗначение = Параметры.Контрагент;
ЭлементОтбора.Использование = Истина;
КонецПроцедуры
Чтобы Параметры.Контрагент существовал, добавьте параметр формы с таким именем на вкладке «Параметры» редактора формы. Без этого обращение к нему вызовет ошибку при открытии.
Способ 2. Стандартный параметр «Отбор» при открытии
Форма списка понимает параметр Отбор без всякого кода внутри неё. Передаёте структуру «поле — значение», и платформа сама ставит фиксированный отбор на основной список формы:
ПараметрыФормы = Новый Структура;
ПараметрыФормы.Вставить("Отбор", Новый Структура("Владелец", Контрагент));
ОткрытьФорму("Справочник.ДоговорыКонтрагентов.ФормаВыбора", ПараметрыФормы, Элемент);
Этот способ хорош, когда одна и та же форма открывается из разных мест с разными условиями. Форму менять не нужно: условие задаёт вызывающий код.
Через структуру удобно задавать простые условия на равенство. Для «больше», «в иерархии», групп «ИЛИ» и прочих сложных условий нужен способ 1 или функция БСП.
Способ 3. Функция БСП
В Бухгалтерии 3.0, ЗУП 3.1, УТ 11 и других конфигурациях на БСП есть готовая функция. Она сама ищет существующий элемент по полю и обновляет его, а не плодит дубли:
ОбщегоНазначенияКлиентСервер.УстановитьЭлементОтбораДинамическогоСписка(
Список,
"ПометкаУдаления",
Ложь,
ВидСравненияКомпоновкиДанных.Равно,
,
Истина);
Порядок параметров: список, имя поля, значение, вид сравнения, представление, использование. Дальше идут необязательные режим отображения и идентификатор пользовательской настройки. Для снятия отбора по полю есть парная процедура ОбщегоНазначенияКлиентСервер.УдалитьЭлементыГруппыОтбораДинамическогоСписка(Список, "ПометкаУдаления").
Сложные условия: группы «ИЛИ», «НЕ» и несколько полей
Все элементы, добавленные в корень отбора, соединяются через «И». Для условия «или» нужна группа элементов отбора.
Пример: показать договоры, у которых срок действия не задан или ещё не истёк.
&НаСервере
Процедура УстановитьОтборПоСроку()
Отбор = Список.КомпоновщикНастроек.ФиксированныеНастройки.Отбор;
Группа = Отбор.Элементы.Добавить(Тип("ГруппаЭлементовОтбораКомпоновкиДанных"));
Группа.ТипГруппы = ТипГруппыЭлементовОтбораКомпоновкиДанных.ГруппаИли;
Группа.Использование = Истина;
Элемент = Группа.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
Элемент.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("СрокДействия");
Элемент.ВидСравнения = ВидСравненияКомпоновкиДанных.НеЗаполнено;
Элемент.Использование = Истина;
Элемент = Группа.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
Элемент.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("СрокДействия");
Элемент.ВидСравнения = ВидСравненияКомпоновкиДанных.БольшеИлиРавно;
Элемент.ПравоеЗначение = НачалоДня(ТекущаяДатаСеанса());
Элемент.Использование = Истина;
КонецПроцедуры
Типы групп три: ГруппаИ, ГруппаИли и ГруппаНе. Группы вкладываются друг в друга, так что любое логическое выражение собирается как дерево.
🔹 Поле реквизита ссылки задаётся через точку: Новый ПолеКомпоновкиДанных("Владелец.ИНН"). Платформа сама добавит соединение.
🔹 Отбор по полю, которого нет в настройках списка, платформа не применит. Сначала проверьте, что поле доступно, например через Список.КомпоновщикНастроек.Настройки.ДоступныеПоляОтбора.НайтиПоле(...).
🔹 Если условие сложно выразить отбором, перейдите на произвольный запрос и поставьте условие с параметром в ГДЕ.
Условие через параметр произвольного запроса
Включите у реквизита Список флажок «Произвольный запрос», напишите в тексте ГДЕ Договоры.Организация = &Организация и задайте значение:
Список.Параметры.УстановитьЗначениеПараметра("Организация", Параметры.Организация);
Значение нужно задать для каждого параметра запроса, иначе список не откроется с ошибкой о неустановленном параметре. Чтобы не терять в скорости, укажите у произвольного запроса основную таблицу и не отключайте динамическое считывание.
Виды сравнения: какой выбрать и что класть в ПравоеЗначение
Ошибка в виде сравнения — самая частая причина «пустого» списка. Свод основных вариантов ВидСравненияКомпоновкиДанных:
| Вид сравнения | Что делает | ПравоеЗначение |
|---|---|---|
Равно / НеРавно | точное совпадение | одно значение |
Больше, БольшеИлиРавно, Меньше, МеньшеИлиРавно | числа и даты | одно значение |
ВСписке / НеВСписке | одно из нескольких | СписокЗначений |
ВИерархии / НеВИерархии | группа и всё, что в ней | ссылка на группу |
ВСпискеПоИерархии | несколько групп с подчинёнными | СписокЗначений групп |
Содержит / НеСодержит | подстрока в строковом поле | строка |
НачинаетсяС / НеНачинаетсяС | начало строки | строка |
Подобно / НеПодобно | шаблон с % и _ | строка-шаблон |
Заполнено / НеЗаполнено | значение задано или пустое | не нужно |
Пример с несколькими значениями:
Статусы = Новый СписокЗначений;
Статусы.Добавить(Перечисления.СтатусыДоговоров.Действует);
Статусы.Добавить(Перечисления.СтатусыДоговоров.НаСогласовании);
Элемент.ВидСравнения = ВидСравненияКомпоновкиДанных.ВСписке;
Элемент.ПравоеЗначение = Статусы;
Для ВСписке передавайте именно СписокЗначений. Это стандартный тип правого значения в компоновке: с ним отбор корректно показывается и в пользовательской настройке списка.
Содержит на большой таблице работает медленно: индекс по строке тут не помогает. Для поиска по наименованию в списке с десятками тысяч строк лучше НачинаетсяС или штатная строка поиска.
Отборы в иерархических справочниках
В справочнике с группами (номенклатура, контрагенты, статьи затрат) отбор ведёт себя иначе, чем в плоском списке. Режим вывода задаётся у элемента формы: Элементы.Список.Отображение принимает ОтображениеТаблицы.Список, ИерархическийСписок или Дерево.
Что учесть:
- 📁 Отбор по группе делайте через
ВИерархиипо полюСсылкаилиРодитель. УсловиеРодитель Равно Группапокажет только непосредственных потомков, без вложенных подгрупп. - 🔍 Отбор по реквизиту элемента (например,
ВидНоменклатуры) в иерархическом режиме оставляет на экране группы, которые по реквизиту не подходят, но содержат подходящие элементы. Пользователь видит «пустые» на вид папки. Если это мешает, переключитеОтображениенаСписокна время отбора. - 🗂 Открыть список сразу внутри группы можно через свойство таблицы
Элементы.Список.ТекущийРодитель. Это не отбор: пользователь может подняться выше. Если выше подниматься нельзя, ставьте фиксированный отборВИерархии. - 🧩 Отбор по полю
ЭтоГруппасо значениемЛожьубирает группы совсем. Удобно для форм подбора, где выбирать можно только элементы.
Как менять отбор на лету и обновлять список
Отбор не обязательно ставить один раз. Частый сценарий: в шапке формы есть поле «Организация», пользователь меняет его, и список тут же перестраивается.
Компоновщик настроек динамического списка доступен и на клиенте, поэтому серверный вызов не нужен:
&НаКлиенте
Процедура ОрганизацияОтборПриИзменении(Элемент)
ОбщегоНазначенияКлиентСервер.УстановитьЭлементОтбораДинамическогоСписка(
Список,
"Организация",
ОрганизацияОтбор,
ВидСравненияКомпоновкиДанных.Равно,
,
ЗначениеЗаполнено(ОрганизацияОтбор));
КонецПроцедуры
Шестой параметр (Использование) решает главную проблему такого поля: когда пользователь очищает «Организацию», отбор выключается, а не начинает искать записи с пустой организацией.
После изменения отбора список перечитывается сам. Явный вызов Элементы.Список.Обновить() нужен, когда изменились данные в базе, а условия остались прежними: например, после записи документа из другой формы фоновым заданием.
Для экранов-мониторов, где данные меняют другие пользователи, у таблицы формы есть автообновление (свойства Автообновление и ПериодАвтообновления). Не ставьте период в несколько секунд на тяжёлом списке: каждое обновление — это новый запрос к базе от каждого открытого окна.
Пользовательский отбор, который не затирается
Если ставить условие в КомпоновщикНастроек.Настройки.Отбор прямо в ПриСозданииНаСервере, оно может пропасть. После этого обработчика платформа загружает сохранённые настройки пользователя и перекрывает ваши.
Решения:
- Отбор, который пользователь менять не должен, — только в
ФиксированныеНастройки. - Отбор по умолчанию, который пользователь может поменять, ставьте в обработчике
ПриЗагрузкеДанныхИзНастроекНаСерверелибо задавайте у элемента идентификатор пользовательской настройки, чтобы он попал в быстрые настройки над списком. - Если сохранённые настройки не нужны вовсе, снимите у таблицы флажок сохранения пользовательских настроек.
Ошибки и ограничения, о которых узнают слишком поздно
- ⛔ Отбор — не защита данных. Пользователь с правом чтения может открыть таблицу через другую форму, отчёт или «Все функции». Если доступ надо реально ограничить, нужны роли и RLS, а не отбор в форме.
- 🐢 Медленный отбор по неиндексированному полю. На таблице в сотни тысяч строк условие по реквизиту без индекса даёт полное сканирование при каждой прокрутке. Включите у реквизита индексирование в конфигураторе, если по нему фильтруют постоянно. Это изменение структуры, оно требует обновления конфигурации базы данных.
- 📏 Огромные списки значений.
ВСпискес тысячами элементов превращается в тяжёлое условие в запросе. В таком случае лучше перенести логику в произвольный запрос с соединением. - 🧮 Отбор по виртуальной таблице. Для остатков и оборотов условие внутри параметров виртуальной таблицы через отбор не задаётся. Его ставят параметром в тексте произвольного запроса.
- 🔀 Дата со временем.
Равнопо полюДатадокумента почти никогда не срабатывает, ведь время у документа обычно не полночь. Для отбора за день ставьте два условия:БольшеИлиРавно НачалоДняиМеньшеИлиРавно КонецДня. - 🧱 Доработка типовой формы. Не правьте модуль формы типовой конфигурации напрямую: это мешает обновлениям. Отбор добавляется в расширении конфигурации через обработчик
ПриСозданииНаСерверес директивой&После.
Частые вопросы
Как снять программный отбор с динамического списка?
Отбор.Элементы и удалите его методом Удалить, либо выставьте Использование = Ложь. В конфигурациях на БСП удобнее процедура УдалитьЭлементыГруппыОтбораДинамическогоСписка.Чем отбор на пустое значение отличается от «НеЗаполнено»?
Равно с пустой ссылкой ловит только пустую ссылку нужного типа. НеЗаполнено срабатывает на любое пустое значение, включая Null после соединения и составные типы, поэтому надёжнее.Можно ли установить отбор динамического списка на клиенте?
ПриИзменении поля формы, без серверного вызова.Почему отбор, переданный через параметр «Отбор» в ОткрытьФорму, не работает?
Как вывести программный отбор в быстрые настройки над списком?
Настройки.Отбор, задайте ему ИдентификаторПользовательскойНастройки и режим отображения БыстрыйДоступ. Тогда отбор появится полем над таблицей и пользователь сможет его менять.