Динамический список в 1С — основной способ вывести записи базы на управляемую форму: платформа сама читает данные порциями, сортирует и группирует их. Но почти любая доработка формы требует управлять динамическим списком программно: отфильтровать документы по складу из шапки, передать в запрос дату, скрыть помеченные на удаление позиции в форме выбора или забрать в код то, что пользователь видит на экране. Ниже — примеры для каждой из этих задач, разбор ключевых строк и ошибки, на которых чаще всего теряют время.
Код рассчитан на платформу 8.3 и любую конфигурацию. Имена справочников и полей (Склад, Номенклатура) условные — подставьте свои. Фрагменты, которым нужна БСП, отмечены отдельно.
У реквизита типа ДинамическийСписок есть компоновщик настроек, как у отчёта на СКД. От того, в какой его слой записано условие, зависит, увидит ли его пользователь и сможет ли снять.
| Куда писать | Что получается | Когда использовать |
|---|---|---|
Список.Отбор | Фиксированный отбор: пользователь не видит его в «Настроить список» и не может отключить | Обязательные условия: отбор по значению из шапки формы, «только своя организация» |
Список.КомпоновщикНастроек.Настройки.Отбор | Отбор в настройках списка: виден пользователю, может сохраняться между сеансами | Нужно предложить условие по умолчанию, но оставить пользователю право его поменять |
Список.КомпоновщикНастроек.ПользовательскиеНастройки | То, что пользователь настроил сам: быстрые отборы, сохранённые варианты | Из кода обычно не трогают |
Свойство Список.Отбор — это отбор фиксированных настроек (КомпоновщикНастроек.ФиксированныеНастройки.Отбор). Отбор по значению с формы пишите туда: условие не сбросится командой «Установить стандартные настройки» и не зависит от сохранённых настроек пользователя. Условие по одному полю держите в одном слое, а не в двух сразу.
Одно условие — это объект ЭлементОтбораКомпоновкиДанных. Нужны пять его свойств: ЛевоеЗначение (поле), ВидСравнения, ПравоеЗначение, Использование и РежимОтображения. Частая ошибка — добавлять новый элемент при каждом изменении поля: после пяти смен склада в отборе висят пять условий, и список пуст. Поэтому процедура ниже сначала ищет существующее условие по полю.
&НаКлиенте
Процедура СкладОтбораПриИзменении(Элемент)
УстановитьОтборСписка(Список, "Склад", СкладОтбора, ЗначениеЗаполнено(СкладОтбора));
КонецПроцедуры
&НаКлиентеНаСервереБезКонтекста
Процедура УстановитьОтборСписка(ДинамическийСписок, ИмяПоля, Значение,
Использование = Истина, ВидСравнения = Неопределено)
Если ВидСравнения = Неопределено Тогда
ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
КонецЕсли;
Поле = Новый ПолеКомпоновкиДанных(ИмяПоля);
ЭлементОтбора = Неопределено;
// Ищем существующее условие по этому полю, чтобы не плодить дубли
Для Каждого ТекущийЭлемент Из ДинамическийСписок.Отбор.Элементы Цикл
Если ТипЗнч(ТекущийЭлемент) = Тип("ЭлементОтбораКомпоновкиДанных")
И ТекущийЭлемент.ЛевоеЗначение = Поле Тогда
ЭлементОтбора = ТекущийЭлемент;
Прервать;
КонецЕсли;
КонецЦикла;
Если ЭлементОтбора = Неопределено Тогда
Если Не Использование Тогда
Возврат; // условия нет и включать его не нужно
КонецЕсли;
ЭлементОтбора = ДинамическийСписок.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбора.ЛевоеЗначение = Поле;
КонецЕсли;
ЭлементОтбора.ВидСравнения = ВидСравнения;
ЭлементОтбора.ПравоеЗначение = Значение;
ЭлементОтбора.Использование = Использование;
ЭлементОтбора.РежимОтображения = РежимОтображенияЭлементаНастройкиКомпоновкиДанных.Недоступный;
КонецПроцедуры
Разбор ключевых строк:
&НаКлиентеНаСервереБезКонтекста. Объекты компоновки (ПолеКомпоновкиДанных, ЭлементОтбораКомпоновкиДанных, ВидСравненияКомпоновкиДанных) доступны на клиенте, поэтому отбор меняется прямо в клиентском обработчике, без своего серверного вызова. Список перечитает данные сам, Элементы.Список.Обновить() не нужен.Новый ПолеКомпоновкиДанных("Склад"). Указывается имя поля динамического списка, а не колонки в таблице базы. Если в произвольном запросе написано Документ.Склад КАК СкладОтгрузки, в отборе пишем СкладОтгрузки. Путь через точку тоже работает: "Контрагент.ИНН".Использование. Когда пользователь очищает поле на форме, условие удобнее выключить, а не удалять, поэтому в вызов передаётся ЗначениеЗаполнено(СкладОтбора).РежимОтображения. Условие в Список.Отбор пользователь и так не видит, а Недоступный — страховка на случай, если процедуру переключат на КомпоновщикНастроек.Настройки.Отбор. Режимы БыстрыйДоступ и Обычный нужны для видимых пользователю условий, о них — в вопросах в конце статьи.Условие можно выключить (Использование = Ложь, как делает процедура выше) или удалить. Удалять элементы прямо в цикле Для Каждого по той же коллекции рискованно, часть может пропуститься, поэтому сначала собираем найденное в массив:
&НаКлиентеНаСервереБезКонтекста
Процедура СнятьОтборСписка(ДинамическийСписок, ИмяПоля)
Поле = Новый ПолеКомпоновкиДанных(ИмяПоля);
ЭлементыОтбора = ДинамическийСписок.Отбор.Элементы;
КУдалению = Новый Массив;
Для Каждого ЭлементОтбора Из ЭлементыОтбора Цикл
Если ТипЗнч(ЭлементОтбора) = Тип("ЭлементОтбораКомпоновкиДанных")
И ЭлементОтбора.ЛевоеЗначение = Поле Тогда
КУдалению.Добавить(ЭлементОтбора);
КонецЕсли;
КонецЦикла;
Для Каждого ЭлементОтбора Из КУдалению Цикл
ЭлементыОтбора.Удалить(ЭлементОтбора);
КонецЦикла;
КонецПроцедуры
Список.Отбор.Элементы.Очистить() тоже снимает отбор, но удаляет все условия подряд, включая поставленные типовым кодом формы или расширением.
Условия верхнего уровня соединяются через «И». Чтобы показать документы по выбранному складу или без склада, нужна группа ГруппаЭлементовОтбораКомпоновкиДанных с типом ГруппаИли. Условия добавляются уже в коллекцию Элементы самой группы:
&НаСервере
Процедура УстановитьОтборСкладИлиПустой(Склад)
Группа = Список.Отбор.Элементы.Добавить(Тип("ГруппаЭлементовОтбораКомпоновкиДанных"));
Группа.ТипГруппы = ТипГруппыЭлементовОтбораКомпоновкиДанных.ГруппаИли;
Группа.Использование = Истина;
// Склад равен выбранному...
ЭлементОтбора = Группа.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Склад");
ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
ЭлементОтбора.ПравоеЗначение = Склад;
ЭлементОтбора.Использование = Истина;
// ...или склад не заполнен
ЭлементОтбора = Группа.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Склад");
ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.НеЗаполнено;
ЭлементОтбора.Использование = Истина;
КонецПроцедуры
Вызывайте её один раз, например в ПриСозданииНаСервере; при смене значения правьте элементы существующей группы, а не добавляйте новую.
Значение ВидСравненияКомпоновкиДанных | Что означает | Что передать в ПравоеЗначение |
|---|---|---|
Равно, НеРавно | точное совпадение | одно значение |
ВСписке, НеВСписке | значение входит в набор | СписокЗначений |
ВИерархии | сама группа и всё, что в неё вложено, на любом уровне | ссылка на группу |
ВСпискеПоИерархии | то же для нескольких групп | СписокЗначений групп |
НеВИерархии, НеВСпискеПоИерархии | исключить группы вместе с содержимым | группа или СписокЗначений групп |
Больше, БольшеИлиРавно, Меньше, МеньшеИлиРавно | сравнение чисел и дат | число или дата |
Содержит, НеСодержит | поиск подстроки | строка |
НачинаетсяС, Подобно и их отрицания | начало строки или шаблон, как у ПОДОБНО в запросе | строка |
Заполнено, НеЗаполнено | проверка на пустое значение | не нужно |
В конфигурациях на Библиотеке стандартных подсистем (УТ 11, ERP, КА 2, Бухгалтерия 3.0) писать поиск элемента самому не нужно. В общем модуле ОбщегоНазначенияКлиентСервер (БСП 3.x) есть процедуры, которые вызываются и на клиенте, и на сервере:
&НаКлиенте
Процедура СкладОтбораПриИзменении(Элемент)
ОбщегоНазначенияКлиентСервер.УстановитьЭлементОтбораДинамическогоСписка(Список,
"Склад", СкладОтбора, ВидСравненияКомпоновкиДанных.Равно, ,
ЗначениеЗаполнено(СкладОтбора));
КонецПроцедуры
&НаКлиенте
Процедура СброситьОтборПоСкладу(Команда)
// Сначала очищаем поле в шапке, иначе оно будет противоречить списку
СкладОтбора = ПредопределенноеЗначение("Справочник.Склады.ПустаяСсылка");
ОбщегоНазначенияКлиентСервер.УдалитьЭлементыГруппыОтбораДинамическогоСписка(Список, "Склад");
КонецПроцедуры
Параметры УстановитьЭлементОтбораДинамическогоСписка по порядку: список, имя поля, правое значение, вид сравнения, представление, использование, режим отображения, идентификатор пользовательской настройки. Существующее условие по полю процедура находит сама, в том числе внутри групп. Слой зависит от режима отображения: при Недоступный (он же по умолчанию) условие пишется в КомпоновщикНастроек.ФиксированныеНастройки.Отбор, то есть в тот же Список.Отбор, что и в «ручном» коде выше, а при БыстрыйДоступ и Обычный — в КомпоновщикНастроек.Настройки.Отбор. УдалитьЭлементыГруппыОтбораДинамическогоСписка чистит оба слоя. Для параметров запроса есть УстановитьПараметрДинамическогоСписка(Список, ИмяПараметра, Значение, Использование).
Если в тексте запроса списка есть параметры (&ДатаОстатков, &ТолькоАктивные), их значения задаются через коллекцию Список.Параметры методом УстановитьЗначениеПараметра. Отбор платформа добавляет к запросу сама, а параметр — это значение для &Имя, которое вы явно написали в тексте запроса. Поэтому только параметром можно передать дату в виртуальную таблицу, например в Остатки(&ДатаОстатков), или значение в условие соединения.
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Параметры задаём до первого вывода списка
ДатаОстатков = ТекущаяДатаСеанса();
// Начало следующего дня: граница «исключая», поэтому в остатки попадут все движения выбранного дня
Список.Параметры.УстановитьЗначениеПараметра("ДатаОстатков", КонецДня(ДатаОстатков) + 1);
Список.Параметры.УстановитьЗначениеПараметра("ТолькоАктивные", Истина);
КонецПроцедуры
&НаКлиенте
Процедура ДатаОстатковПриИзменении(Элемент)
// Та же граница: начало следующего дня
Список.Параметры.УстановитьЗначениеПараметра("ДатаОстатков", КонецДня(ДатаОстатков) + 1);
КонецПроцедуры
Первое значение задайте в ПриСозданииНаСервере: пока параметр пуст, список не покажет ни одной строки. Потом его можно менять и на клиенте, список перечитается сам. Имя параметра пишется без амперсанда. Зачем + 1: дата в параметре Остатки — граница «исключая», и движения ровно в 23:59:59 в остаток на КонецДня не попадут.
Иногда запрос списка зависит от условий, известных только при открытии формы: колонка с ценой нужна не всем, соединение с регистром — только при включённой функциональной опции. Тогда текст запроса задают на сервере:
&НаСервере
Процедура НастроитьЗапросСписка()
// Имена справочника и регистра условные - подставьте объекты своей конфигурации
ТекстЗапроса =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка,
| Номенклатура.Наименование КАК Наименование,
| Номенклатура.Артикул КАК Артикул,
| ЕСТЬNULL(Цены.Цена, 0) КАК Цена
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&ДатаЦен, ВидЦены = &ВидЦены) КАК Цены
| ПО Цены.Номенклатура = Номенклатура.Ссылка";
Список.ПроизвольныйЗапрос = Истина;
Список.ТекстЗапроса = ТекстЗапроса;
Список.ОсновнаяТаблица = "Справочник.Номенклатура";
Список.Параметры.УстановитьЗначениеПараметра("ДатаЦен", ТекущаяДатаСеанса());
Список.Параметры.УстановитьЗначениеПараметра("ВидЦены", ВидЦеныОтбора);
КонецПроцедуры
Порядок: включить ПроизвольныйЗапрос, присвоить ТекстЗапроса, указать ОсновнаяТаблица — без неё не работают стандартные команды списка (открыть, скопировать, пометить на удаление). После смены текста заново установите параметры и отборы.
Соединение должно давать не больше одной строки на Номенклатура.Ссылка: по этому ключу список различает строки. Если у регистра цен есть другие измерения (например, Характеристика), зафиксируйте их в условии виртуальной таблицы или соберите цену вложенным запросом, иначе в списке появятся дубли.
В БСП 3.x то же делается одним вызовом. Первым параметром процедура ждёт элемент формы, а не реквизит, — реквизит она находит по ПутьКДанным. Флаг ПроизвольныйЗапрос она не меняет: он должен быть включён у реквизита.
&НаСервере
Процедура УстановитьЗапросСпискаБСП(ТекстЗапроса)
Свойства = ОбщегоНазначения.СтруктураСвойствДинамическогоСписка();
Свойства.ТекстЗапроса = ТекстЗапроса;
Свойства.ОсновнаяТаблица = "Справочник.Номенклатура";
// Элемент формы (ТаблицаФормы), а не реквизит Список
ОбщегоНазначения.УстановитьСвойстваДинамическогоСписка(Элементы.Список, Свойства);
КонецПроцедуры
Другой случай — ограничить список в форме выбора, которая открывается из поля ввода: в поле «Договор» показывать только договоры выбранного контрагента, в поле «Номенклатура» — не показывать помеченные на удаление позиции. Список здесь в другой форме, и отбор передаётся ей через параметры.
Нередко хватает настроек без кода: в свойствах поля заполнить «Связи параметров выбора» (Отбор.Владелец → Объект.Контрагент) или «Параметры выбора» с постоянным значением, а выбор групп запретить свойством поля «Выбор групп и элементов». Когда условие вычисляется в коде, параметры выбора задают программно, а в нестандартных случаях открывают форму выбора сами в обработчике НачалоВыбора:
&НаСервере
Процедура НастроитьВыборНоменклатуры()
// Копируем параметры выбора, уже заданные у поля в конфигураторе или типовым кодом
ПараметрыВыбораПоля = Новый Массив(Элементы.Номенклатура.ПараметрыВыбора);
// В форме выбора не показывать позиции, помеченные на удаление
ПараметрыВыбораПоля.Добавить(Новый ПараметрВыбора("Отбор.ПометкаУдаления", Ложь));
Элементы.Номенклатура.ПараметрыВыбора = Новый ФиксированныйМассив(ПараметрыВыбораПоля);
КонецПроцедуры
&НаКлиенте
Процедура ДоговорНачалоВыбора(Элемент, ДанныеВыбора, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
ПараметрыФормыВыбора = Новый Структура;
ПараметрыФормыВыбора.Вставить("Отбор", Новый Структура("Владелец", Объект.Контрагент));
ОткрытьФорму("Справочник.ДоговорыКонтрагентов.ФормаВыбора", ПараметрыФормыВыбора, Элемент);
КонецПроцедуры
Присваивание ПараметрыВыбора заменяет массив целиком, поэтому сначала копируем то, что уже задано у поля, иначе в типовой форме пропадут ограничения вроде Отбор.ЭтоГруппа.
Ключ Отбор в параметрах формы — стандартный: платформа применит его к основному динамическому списку формы выбора, и пользователь не сможет это условие отключить. Третий параметр ОткрытьФорму — владелец, в его поле платформа сама подставит выбранное значение. В своей форме выбора структуру можно прочитать в ПриСозданииНаСервере через Параметры.Отбор.
Чтобы получить в коде данные списка такими, как их видит пользователь (с его отборами, поиском и сортировкой), запрос вручную собирать не нужно. С версии 8.3.6 у таблицы формы с динамическим списком есть методы ПолучитьИсполняемуюСхемуКомпоновкиДанных() и ПолучитьИсполняемыеНастройкиКомпоновкиДанных(): они возвращают схему и настройки, по которым платформа строит список. Дальше — обычный вывод компоновки в коллекцию значений:
&НаСервере
Функция ДанныеСпискаВТаблицуЗначений()
// Схема и настройки, по которым платформа строит список (с учетом всех отборов)
Схема = Элементы.Список.ПолучитьИсполняемуюСхемуКомпоновкиДанных();
Настройки = Элементы.Список.ПолучитьИсполняемыеНастройкиКомпоновкиДанных();
// Подстраховка: если в настройках нет структуры, выводим детальные записи
Если Настройки.Структура.Количество() = 0 Тогда
Группировка = Настройки.Структура.Добавить(Тип("ГруппировкаКомпоновкиДанных"));
Группировка.Выбор.Элементы.Добавить(Тип("АвтоВыбранноеПолеКомпоновкиДанных"));
КонецЕсли;
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
Макет = КомпоновщикМакета.Выполнить(Схема, Настройки, , ,
Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений"));
Процессор = Новый ПроцессорКомпоновкиДанных;
Процессор.Инициализировать(Макет);
Результат = Новый ТаблицаЗначений;
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
ПроцессорВывода.УстановитьОбъект(Результат);
ПроцессорВывода.Вывести(Процессор);
Возврат Результат;
КонецФункции
Ограничения этого способа:
Элементы.Список, а не у реквизита Список. Компоновщик макета и процессор компоновки на клиенте недоступны, поэтому вся функция — &НаСервере.Настройки.Выбор перед компоновкой.Если нужны только выделенные пользователем строки, компоновка не нужна: массив ключей (для справочников и документов — ссылок) есть в Элементы.Список.ВыделенныеСтроки прямо на клиенте.
Новый КомпоновщикМакетаКомпоновкиДанных в клиентской процедуре даёт ошибку «Тип не определен (КомпоновщикМакетаКомпоновкиДанных)». Выгрузку делайте на сервере.КомпоновщикНастроек.Настройки.Отбор, а при открытии платформа восстановила сохранённые настройки пользователя со старым значением. Обязательные условия ставьте через Список.Отбор.ТекстЗапроса: параметры нового запроса задайте заново.Отбор — условие, которое платформа сама добавляет к запросу списка; его можно включать и выключать, а пользователь может видеть его в настройках. Параметр — значение, которое передаётся в запрос для &Имя. Для фильтра по полю берите отбор, для даты виртуальной таблицы или значения в соединении — параметр.
Вида «Между» в перечислении нет, поэтому период собирают из двух условий по одному полю: БольшеИлиРавно с началом периода и МеньшеИлиРавно с концом (для дат удобно КонецДня). Процедура УстановитьОтборСписка из статьи ищет условие только по имени поля, поэтому для периода её нужно доработать — искать ещё и по виду сравнения — или добавлять оба элемента напрямую.
Это фиксированные настройки, в «Настроить список» они не показываются. Чтобы условие было видно и его можно было менять, пишите его в КомпоновщикНастроек.Настройки.Отбор с режимом отображения БыстрыйДоступ и заполненным идентификатором пользовательской настройки — или вызовите процедуру БСП с этими параметрами.
Элементы.Список.ТекущиеДанные возвращает значения колонок текущей строки (на клиенте), Элементы.Список.ТекущаяСтрока — её ключ; для справочника или документа это ссылка. Для нескольких выделенных строк есть ВыделенныеСтроки.
Отбор в типовой форме УТ, ERP или Бухгалтерии, который должен пережить обновление, обычно делают расширением, и там легко столкнуться с типовым кодом, который сам управляет отборами списка. Такую задачу удобно отдать программисту: опишите, что и как должно фильтроваться, на странице размещения задачи — оценка бесплатная, ответ придёт в течение рабочего дня. Другие направления работ собраны на странице услуг по доработке 1С.
Станьте частью сообщества!
Войдите или зарегистрируйтесь, и вы сможете участвовать в обсуждениях.