Как передать таблицу значений в динамический список 1С

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

Работа с динамическими списками в 1С — это стандарт де-факто для отображения больших массивов данных. Однако разработчики часто сталкиваются с жестким ограничением платформы: в динамический список (ДС) нельзя напрямую передать ТаблицуЗначений в качестве параметра запроса, как это делается в обычном объекте Запрос. Динамический список ориентирован на работу с данными, которые физически находятся в таблицах базы данных (СУБД).

В этой статье мы подробно проанализируем ситуацию, выясним причины ограничений и разберем несколько эффективных стратегий решения этой задачи, начиная от использования специальных событий формы и заканчивая архитектурными хитростями с регистрами сведений.

Почему стандартный метод &ПараметрТЗ не работает в динамическом списке

Рассмотрим природу динамического списка. Его главная задача — обеспечивать «динамическую» подгрузку данных порциями (по 20-40 строк) по мере прокрутки списка пользователем. Когда мы используем обычный запрос, платформа 1С берет нашу ТаблицуЗначений, временно загружает ее в СУБД, выполняет запрос и возвращает результат. В случае с динамическим списком запрос выполняется многократно, с разными отборами, сортировками и условиями ГДЕ, которые накладывает платформа или пользователь.

Передача ТаблицыЗначений в параметр &ПараметрТЗ внутри текста запроса динамического списка вызывает ошибку «Неверные параметры», так как механизм ДС не умеет автоматически создавать временную таблицу в СУБД из объекта в памяти при каждой итерации подгрузки данных. Даже если версия вашей платформы выше 8.3.9, где поддерживаются пакетные запросы, это не снимает ограничения на тип передаваемого параметра.

Решение 1: Использование события ПриПолученииДанныхНаСервере

Это наиболее современный и рекомендуемый способ, если ваша цель — дополнить существующие данные из базы какими-то расчетами или данными из внешней таблицы. Мы не «засовываем» таблицу значений в запрос, а «подмешиваем» данные из нее в уже выбранные строки списка.

Алгоритм действий:

  1. Добавим в динамический список колонку, которая не связана с данными (просто добавим в запрос ДС поле-пустышку, например, "" КАК ДопДанные).
  2. В свойствах таблицы формы включим использование события ПриПолученииДанныхНаСервере.
  3. Передадим нашу исходную таблицу значений в «контейнер», доступный этому событию (через ДополнительныеСвойства).

Разберем пример кода. Допустим, у нас есть таблица остатков, которую мы хотим отобразить рядом со списком номенклатуры:


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    
    // Подготовим таблицу значений (например, расчетные остатки)
    ТЗ_Остатки = ПолучитьТаблицуОстатков(); 
    
    // Чтобы передать ТЗ в обработчик события, используем ДополнительныеСвойства компоновщика
    // Предварительно упакуем в ХранилищеЗначения для надежности
    Список.КомпоновщикНастроек.Настройки.ДополнительныеСвойства.Вставить("МоиДанные", Новый ХранилищеЗначения(ТЗ_Остатки));
    
КонецПроцедуры

&НаСервереБезКонтекста
Процедура СписокПриПолученииДанныхНаСервере(ИмяЭлемента, Настройки, Строки)
    
    // Получаем нашу ТЗ из настроек
    ОбъектХранилище = Неопределено;
    Настройки.ДополнительныеСвойства.Свойство("МоиДанные", ОбъектХранилище);
    
    Если ОбъектХранилище = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    ТЗ_Остатки = ОбъектХранилище.Получить();
    
    // Проходим по строкам, которые платформа подготовила для вывода на экран
    Для Каждого СтрокаСписка Из Строки Цикл
        
        // Ищем данные в нашей ТЗ по ключевому полю (например, Ссылка на номенклатуру)
        НайденнаяСтрока = ТЗ_Остатки.Найти(СтрокаСписка.Ключ, "Номенклатура");
        
        Если НайденнаяСтрока <> Неопределено Тогда
            // Заполняем колонку "ДопДанные", которую мы создали в запросе
            СтрокаСписка.Данные.ДопДанные = НайденнаяСтрока.Остаток;
        КонецЕсли;
        
    КонецЦикла;
    
КонецПроцедуры

Важный нюанс: Данные, добавленные таким способом, предназначены только для визуализации. По ним не будет работать стандартный поиск 1С, автоматическая группировка или сортировка, так как этих данных нет в основном запросе СУБД.

Решение 2: Использование буферного регистра сведений

Если вам критически важно, чтобы по данным из ТаблицыЗначений работали фильтры, поиски и итоги внутри динамического списка, придется использовать «физический» посредник в базе данных.

Проанализируем этот подход. Мы создаем технический Регистр Сведений (например, ВременныеДанныеСписков). Его структура должна включать:

Разберем по шагам реализацию:

  1. Перед открытием формы или при обновлении данных мы очищаем старые записи по нашему ИдентификаторуСеанса.
  2. Записываем данные из ТаблицыЗначений в этот регистр.
  3. В динамическом списке используем запрос с ВНУТРЕННИМ СОЕДИНЕНИЕМ между основной таблицей и этим регистром.
  4. Устанавливаем параметр ИдентификаторСеанса в динамическом списке.

Пример запроса динамического списка:


ВЫБРАТЬ
    СпрНоменклатура.Ссылка,
    СпрНоменклатура.Наименование,
    ЕСТЬNULL(ВремДанные.Остаток, 0) КАК ОстатокДляСортировки
ИЗ
    Справочник.Номенклатура КАК СпрНоменклатура
ВНУТРЕННЕЕ СОЕДИНЕНИЕ 
    РегистрСведений.ВременныеДанныеСписков КАК ВремДанные
ПО 
    СпрНоменклатура.Ссылка = ВремДанные.Объект
ГДЕ
    ВремДанные.ИдентификаторСеанса = &ПараметрСеанса

Этот метод работает медленнее из-за записи в БД, но он превращает вашу ТЗ в полноценный источник данных для динамического списка со всеми возможностями платформы.

Решение 3: Динамическое формирование текста запроса (метод UNION ALL)

Рассмотрим экзотический, но рабочий метод для небольших таблиц (до 50-100 строк). Если данных мало, мы можем программно сконструировать текст запроса, в котором каждая строка таблицы значений будет представлена как отдельная выборка через ОБЪЕДИНИТЬ ВСЕ.

Проанализируем пример формирования такого «страшного» запроса:


ТекстЗапроса = "";
Для Каждого СтрокаТЗ Из МояТаблицаЗначений Цикл
    
    НомерСтроки = МояТаблицаЗначений.Индекс(СтрокаТЗ);
    Если НомерСтроки > 0 Тогда
        ТекстЗапроса = ТекстЗапроса + " ОБЪЕДИНИТЬ ВСЕ ";
    КонецЕсли;
    
    ТекстЗапроса = ТекстЗапроса + 
        "ВЫБРАТЬ &Имя" + НомерСтроки + " КАК Имя, &Значение" + НомерСтроки + " КАК Значение";
    
    // Устанавливаем параметры в динамический список
    Список.Параметры.УстановитьЗначениеПараметра("Имя" + НомерСтроки, СтрокаТЗ.Имя);
    Список.Параметры.УстановитьЗначениеПараметра("Значение" + НомерСтроки, СтрокаТЗ.Значение);
    
КонецЦикла;

Список.ТекстЗапроса = ТекстЗапроса;

Предупреждение: Этот метод крайне ограничен. При большом количестве строк текст запроса станет гигантским, что приведет к ошибке переполнения стека парсера SQL или катастрофическому падению производительности. Используйте его только в исключительных случаях для микро-таблиц.

Подведение итогов

Мы выяснили, что прямой передачи ТЗ в динамический список не существует. Однако, выбирая между предложенными вариантами, придерживайтесь следующих правил:

Помните, что динамический список — это инструмент для работы с базой данных, а таблица значений — инструмент для работы в оперативной памяти. Грамотное разделение этих сущностей поможет вам создавать быстрые и надежные интерфейсы.

← На главную