При разработке интерфейсов на платформе 1С программисты часто сталкиваются с задачей отображения данных, которые невозможно получить простым запросом к базе данных. Это могут быть результаты сложных расчетов, данные из внешних систем или предварительно обработанные массивы информации, хранящиеся в ТаблицеЗначений (ТЗ). Однако ДинамическийСписок (ДС) по своей архитектуре предназначен для работы с данными, хранящимися непосредственно в СУБД.
Рассмотрим подробнее, почему возникает эта сложность и какими способами можно вывести содержимое ТЗ в динамический список, учитывая современные возможности платформы и классические подходы.
Разберем ситуацию: динамический список работает по принципу порционного считывания данных. Когда пользователь прокручивает список, система генерирует SQL-запрос к базе данных. ТаблицаЗначений же является объектом, существующим исключительно в оперативной памяти сервера 1С. SQL-сервер ничего не знает о данных в памяти процесса rphost, поэтому он не может выполнить соединение физической таблицы базы данных с вашей ТЗ.
Именно поэтому стандартный ответ на вопрос "можно ли просто передать ТЗ в ДС" — "нет, это противоречит сути динамического списка". Тем не менее, существует несколько методов реализации этой задачи, которые мы проанализируем ниже.
Начиная с версии платформы 8.3.15, появилась возможность использовать таблицу значений в качестве параметра запроса в динамическом списке. Это наиболее близкий к "прямому" способ, хотя он имеет ряд существенных нюансов. Выясним причину, по которой этот метод часто вызывает ошибки у новичков.
Для реализации этого метода необходимо выполнить два условия: жесткая типизация колонок ТЗ и правильная установка параметра. Проанализируем пример кода:
// 1. Создаем ТЗ с жестким описанием типов
ОписаниеСтр = Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(100));
ОписаниеСс = Новый ОписаниеТипов("СправочникСсылка.Номенклатура");
ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Add("Наименование", ОписаниеСтр);
ТЗ.Колонки.Add("Товар", ОписаниеСс);
// Наполняем ТЗ данными...
НоваяСтрока = ТЗ.Добавить();
НоваяСтрока.Наименование = "Тестовый товар";
НоваяСтрока.Товар = Справочники.Номенклатура.НайтиПоНаименованию("Товар 1");
// 2. Устанавливаем текст запроса в динамическом списке
Список.ТекстЗапроса = "ВЫБРАТЬ Т.Наименование, Т.Товар ИЗ &ВнешняяТЗ КАК Т";
// 3. Передаем ТЗ в параметры
ПараметрТЗ = Список.Параметры.Элементы.Найти("ВнешняяТЗ");
Если ПараметрТЗ = Неопределено Тогда
Список.Параметры.УстановитьЗначениеПараметра("ВнешняяТЗ", ТЗ);
КонецЕсли;
Важный нюанс: Если вы не зададите типы колонок через ОписаниеТипов, платформа не сможет сопоставить типы данных при генерации внутреннего запроса, и список останется пустым или выдаст ошибку. Кроме того, помните, что при каждом обновлении списка (например, при листании) вся ваша ТЗ будет заново передаваться на сервер СУБД, что может негативно сказаться на производительности при больших объемах данных.
Если ваша задача заключается не в полной замене источника данных, а лишь в дополнении списка информацией из ТЗ (например, расчетными остатками или статусами), мы рекомендуем использовать обработчик ПриПолученииДанныхНаСервере. Посмотрим на алгоритм реализации:
Рассмотрим пример реализации:
&НаСервере
Процедура СписокПриПолученииДанныхНаСервере(ИмяЭлемента, Настройки, Строки)
// Получаем массив ссылок, которые сейчас отображаются на экране
МассивСсылок = Строки.ПолучитьКлючи();
// Ищем данные в нашей заранее подготовленной ТЗ (хранится в реквизите формы)
Для Каждого СтрокаСписка Из Строки Цикл
ДанныеИзТЗ = Объект.МояТЗ.Найти(СтрокаСписка.Ключ, "Ссылка");
Если ДанныеИзТЗ <> Неопределено Тогда
// Заполняем расчетные данные в оформление строки
СтрокаСписка.Значение.Данные[ИмяНужнойКолонки] = ДанныеИзТЗ.Значение;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Этот метод считается более "правильным" с точки зрения архитектуры, так как он сохраняет все преимущества динамического списка: быструю сортировку и отбор по ключевым полям БД, при этом позволяя подмешивать произвольные данные из памяти.
В ситуациях, когда по данным из ТЗ требуется выполнять полноценный поиск, накладывать сложные фильтры или делать сортировку, предыдущие методы не помогут. В этом случае мы должны признать, что "динамический" список требует "статических" данных в базе.
Проанализируем шаги реализации этого метода:
РегистрСведений или Справочник (например, "БуферДанныхСписков").ИдентификаторСеанса (или Пользователь), чтобы данные разных пользователей не перемешивались.ИдентификаторуСеанса.Этот метод — единственный способ получить полноценный функционал динамического списка (Ctrl+F, настройка группировок пользователем) для данных, которые изначально были в ТаблицеЗначений.
Выясним, какой метод выбрать в зависимости от ваших целей:
ТаблицаЗначений.Выбор правильного инструмента позволит избежать "извращений" в коде и обеспечит стабильную работу интерфейса для конечного пользователя.