Как установить текущую строку по ссылке в динамическом списке с произвольным запросом?

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

При разработке интерфейсов на управляемых формах 1С часто возникает задача: отобразить сложные данные из нескольких источников в виде единого списка и при этом иметь возможность программно спозиционировать курсор на конкретном документе или элементе справочника. Однако если динамический список использует произвольный запрос без указания Основной таблицы, стандартное присвоение ссылки в свойство ТекущаяСтрока не срабатывает. Разберем причины такого поведения платформы и рассмотрим рабочие способы решения этой задачи.

Выясним причину проблемы. Когда у динамического списка задана Основная таблица, платформа точно знает тип ключевого поля (например, ДокументСсылка.ЗаказКлиента) и использует его в качестве идентификатора строки. В этом случае для позиционирования достаточно выполнить код вида Элементы.Список.ТекущаяСтрока = НужнаяСсылка;.

Если же запрос объединяет несколько таблиц и основная таблица не выбрана, свойство ТекущаяСтрока таблицы формы принимает целочисленное значение (числовой индекс записи в клиентской выборке). Напрямую передать туда ссылку нельзя. Кроме того, динамический список считывает данные порциями: на стороне клиента физически нет полного массива строк, поэтому найти индекс произвольной ссылки стандартными методами клиента невозможно (метод НайтиПоИдентификатору() доступен только для коллекций данных формы, таких как ТаблицаЗначений).

Способ 1. Программное вычисление индекса строки через СКД на сервере

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

Разберем по шагам реализацию этого механизма в модуле управляемой формы:


&НаСервере
Функция СтрокиДСНаСервере(ТекДок)
    
    // Получаем текущую схему и пользовательские настройки динамического списка
    Схема = ЭтаФорма.Элементы.Заказы.ПолучитьИсполняемуюСхемуКомпоновкиДанных();
    Настройки = ЭтаФорма.Элементы.Заказы.ПолучитьИсполняемыеНастройкиКомпоновкиДанных();
    
    КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
    МакетКомпоновки = КомпоновщикМакета.Выполнить(Схема, Настройки, , , Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений"));
    
    ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
    ПроцессорКомпоновки.Инициализировать(МакетКомпоновки);
    
    ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
    СтрокиДС = ПроцессорВывода.Вывести(ПроцессорКомпоновки);
    
    ТекСтр = Неопределено;
    
    // Ищем строку с целевой ссылкой и определяем её индекс
    Для Каждого Стр Из СтрокиДС Цикл
        Если Стр.Заказ = ТекДок Тогда
            ТекСтр = СтрокиДС.Индекс(Стр);
            Прервать;
        КонецЕсли;
    КонецЦикла;
    
    Возврат ТекСтр;
    
КонецФункции

&НаКлиенте
Процедура ДСУстановитьТекСтр(ТекСсылка)
    
    Если Не ЗначениеЗаполнено(ТекСсылка) Тогда
        Возврат;
    КонецЕсли;
    
    ТекСтр = СтрокиДСНаСервере(ТекСсылка);
    
    Если ТекСтр = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    // Устанавливаем позицию в таблице формы (с учетом смещения индексации)
    ЭтаФорма.Элементы.Заказы.ТекущаяСтрока = ТекСтр + 1;
    
КонецПроцедуры

Проанализируем важные особенности и ограничения этого подхода:

  1. Нагрузка на сервер: Метод полностью считывает результат запроса в оперативную память. Если динамический список содержит десятки или сотни тысяч записей, выполнение такого кода может приводить к заметным задержкам.
  2. Рассинхронизация данных: Если между моментом выполнения кода на сервере и отрисовкой на клиенте список был интерактивно пересортирован или данные в базе изменились, вычисленный индекс может указать на другую строку.

Способ 2. Использование Журнала документов

Если динамический список объединяет документы разных видов (например, ЗаказПокупателя, ЗаказПоставщику, СчетНаОплату), более надежным архитектурным решением является создание объекта метаданных Журнал документов.

Посмотрим на преимущества этого варианта:

  1. Журнал документов на уровне платформы оптимизирован для объединения разнородных документов.
  2. Журнал можно назначить в качестве Основной таблицы динамического списка.
  3. Свойство ТекущаяСтрока снова начнет принимать стандартные ссылки любого из входящих в журнал документов без необходимости писать серверный код поиска по индексам.

Способ 3. Использование служебного регистра сведений

Когда требуется объединять не только документы, но и другие типы данных (например, элементы справочников, регистры или внешние идентификаторы), данные можно агрегировать в периодическом или непериодическом регистре сведений.

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


КлючЗаписи = РегистрыСведений.ОчередьОбработки.СоздатьКлючЗаписи(Новый Структура("Документ, Идентификатор", СсылкаНаОбъект, УникальныйИД));
Элементы.Список.ТекущаяСтрока = КлючЗаписи;

Способ 4. Замена динамического списка на таблицу формы

Если объем выводимых данных небольшой (до нескольких тысяч строк), динамический список целесообразно заменить на реквизит формы с типом ТаблицаЗначений. В этом сценарии:

  1. Запрос выполняется один раз при открытии формы или нажатии кнопки обновления, и данные помещаются в коллекцию ДанныеФормыКоллекция.
  2. Поиск выполняется стандартным методом НайтиСтроки() на сервере или клиенте.
  3. Установка позиции курсора происходит штатно через присвоение идентификатора строки: Элементы.Таблица.ТекущаяСтрока = НайденнаяСтрока.ПолучитьИдентификатор();.

Способ 5. Установка отбора вместо позиционирования

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


&НаКлиенте
Процедура ОтобратьПоСсылке(ЦелеваяСсылка)
    
    ЭлементОтбора = Неопределено;
    Для Каждого Элемент Из Список.Отбор.Элементы Цикл
        Если Элемент.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Заказ") Тогда
            ЭлементОтбора = Элемент;
            Прервать;
        КонецЕсли;
    КонецЦикла;
    
    Если ЭлементОтбора = Неопределено Тогда
        ЭлементОтбора = Список.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
        ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Заказ");
    КонецЕсли;
    
    ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
    ЭлементОтбора.ПравоеЗначение = ЦелеваяСсылка;
    ЭлементОтбора.Использование = Истина;
    
КонецПроцедуры
← На главную