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