Как вывести данные, полученные запросом на сервере, в табличную часть формы 1С при выборе элемента списка?

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← К списку

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

Основное решение: Передача ТаблицыЗначений через реквизит формы

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

Шаг 1: Создание реквизита формы и элемента управления

  1. Добавление реквизита формы: Откройте форму списка справочника (например, Номенклатура) в конфигураторе. В дереве реквизитов формы добавьте новый реквизит. Присвойте ему понятное имя, например, ДанныеПоНоменклатуре. Установите тип этого реквизита как ТаблицаЗначений.

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

  3. Размещение элемента управления на форме: Перетащите созданный реквизит ДанныеПоНоменклатуре из дерева реквизитов на макет формы. Система автоматически создаст элемент формы типа Таблица, связанный с этим реквизитом. Настройте его внешний вид и порядок колонок по вашему усмотрению.

Шаг 2: Заполнение ТаблицыЗначений на сервере

Теперь, когда реквизит формы готов, нам нужно заполнить его данными с сервера. Мы рассмотрим пример, когда это происходит при активации строки в основном списке.

  1. Обработчик события ПриАктивизацииСтроки: На форме списка справочника найдите элемент, представляющий основной список (часто это Список). В его свойствах найдите событие ПриАктивизацииСтроки и создайте для него обработчик. Этот обработчик будет выполняться каждый раз, когда пользователь выбирает новую строку в списке.

  2. Вызов серверной процедуры: Из клиентского обработчика события нам нужно вызвать серверную процедуру для выполнения запроса и получения данных. Передайте в нее ссылку на текущий элемент номенклатуры.

  3. Серверная логика: В серверной процедуре мы выполняем запрос. Главное отличие от первоначального подхода заключается в том, что мы не создаем новую ТаблицаЗначений на сервере (`ТаблицаДанных = Новый ТаблицаЗначений;`), а напрямую работаем с реквизитом формы. После выполнения запроса мы просто выгружаем результат запроса в наш реквизит формы ДанныеПоНоменклатуре, используя метод Загрузить().

Давайте посмотрим на пример кода, который объединяет клиентскую и серверную части, основываясь на вашем запросе:


// В модуле формы справочника Номенклатура

// Клиентская процедура, вызываемая при активации строки списка
&НаКлиенте
Процедура СписокПриАктивизацииСтроки(Элемент)
    // Получаем текущие данные выбранной строки
    ТекущиеДанныеНоменклатуры = Элементы.Список.ТекущиеДанные;

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

// Серверная процедура для получения и загрузки данных
&НаСервере
Процедура ПолучениеДанныхНоменклатурыНаСервере(НоменклатураСсылка)

    // Создаем новый запрос
    Запрос = Новый Запрос;
    Запрос.Текст =
    "ВЫБРАТЬ
    |    ТоварыНаСкладахОстатки.Номенклатура КАК Номенклатура,
    |    ТоварыНаСкладахОстатки.Склад КАК Склад,
    |    СУММА(ТоварыНаСкладахОстатки.КоличествоОстаток) КАК Количество,
    |    СУММА(ТоварыНаСкладахОстатки.СуммаОстаток) КАК Сумма,
    |    ПоступлениеТоваровИУслуг.Контрагент КАК Контрагент,
    |    ПоступлениеТоваровИУслуг.Дата КАК ДатаПрихода,
    |    ПоступлениеТоваровИУслуг.Ссылка КАК ДокументПрихода,
    |    ЦеныНоменклатуры.Цена КАК Цена
    |ИЗ
    |    РегистрНакопления.ТоварыНаСкладах.Остатки КАК ТоварыНаСкладахОстатки
    |        ПОЛНОЕ СОЕДИНЕНИЕ Документ.ПоступлениеТоваровИУслуг КАК ПоступлениеТоваровИУслуг
    |        ПО ТоварыНаСкладахОстатки.Номенклатура = ПоступлениеТоваровИУслуг.Товары.Номенклатура
    |            И ТоварыНаСкладахОстатки.Склад = ПоступлениеТоваровИУслуг.Склад
    |        ПОЛНОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры КАК ЦеныНоменклатуры
    |        ПО ТоварыНаСкладахОстатки.Номенклатура = ЦеныНоменклатуры.Номенклатура
    |ГДЕ
    |    ТоварыНаСкладахОстатки.Номенклатура = &Номенклатура
    |
    |СГРУППИРОВАТЬ ПО
    |    ТоварыНаСкладахОстатки.Номенклатура,
    |    ТоварыНаСкладахОстатки.Склад,
    |    ПоступлениеТоваровИУслуг.Контрагент,
    |    ПоступлениеТоваровИУслуг.Ссылка,
    |    ПоступлениеТоваровИУслуг.Дата,
    |    ЦеныНоменклатуры.Цена";

    // Устанавливаем параметр запроса
    Запрос.УстановитьПараметр("Номенклатура", НоменклатураСсылка);

    // Выполняем запрос
    РезультатЗапроса = Запрос.Выполнить();

    // Очищаем текущие данные в реквизите формы перед загрузкой новых
    ЭтотОбъект.ДанныеПоНоменклатуре.Очистить();

    // Выгружаем результат запроса напрямую в реквизит формы
    // Важно: имена колонок в запросе должны совпадать с именами колонок реквизита ДанныеПоНоменклатуре
    Если НЕ РезультатЗапроса.Пустой() Тогда
        РезультатЗапроса.Выгрузить(ЭтотОбъект.ДанныеПоНоменклатуре);
    КонецЕсли;

КонецПроцедуры

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

Альтернативное решение: Динамический список с параметром

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

Принцип работы

  1. Создание динамического списка: На форме создаем новый реквизит типа ДинамическийСписок. В его свойствах указываем основной запрос, который будет получать данные (например, из регистра накопления ТоварыНаСкладах). Этот запрос должен содержать параметр для отбора по номенклатуре.

  2. Установка параметра при активации строки: В обработчике события ПриАктивизацииСтроки основного списка (клиентская процедура) мы будем устанавливать параметр или отбор для нашего динамического списка. Это приведет к автоматическому обновлению данных в динамическом списке на клиенте, без явной передачи ТаблицыЗначений.

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

Пример настройки динамического списка

Допустим, у нас есть реквизит формы СписокПриходов типа ДинамическийСписок.

В его свойствах на вкладке "Запрос" мы указываем текст запроса:


ВЫБРАТЬ
    ПоступлениеТоваровИУслуг.Ссылка КАК ДокументПрихода,
    ПоступлениеТоваровИУслуг.Дата КАК ДатаПрихода,
    ПоступлениеТоваровИУслуг.Контрагент КАК Контрагент,
    ПоступлениеТоваровИУслуг.Товары.Количество КАК Количество, // Примерное поле
    ПоступлениеТоваровИУслуг.Склад КАК Склад
ИЗ
    Документ.ПоступлениеТоваровИУслуг КАК ПоступлениеТоваровИУслуг
        ЛЕВОЕ СОЕДИНЕНИЕ Документ.ПоступлениеТоваровИУслуг.Товары КАК ПоступлениеТоваровИУслугТовары
        ПО ПоступлениеТоваровИУслуг.Ссылка = ПоступлениеТоваровИУслугТовары.Ссылка
ГДЕ
    ПоступлениеТоваровИУслугТовары.Номенклатура = &Номенклатура

На вкладке "Параметры" динамического списка указываем параметр Номенклатура.

Затем в обработчике события ПриАктивизацииСтроки основного списка:


// В модуле формы справочника Номенклатура

&НаКлиенте
Процедура СписокПриАктивизацииСтроки(Элемент)
    ТекущаяНоменклатура = Элементы.Список.ТекущиеДанные.Ссылка;

    // Устанавливаем параметр для динамического списка
    Элементы.СписокПриходов.Параметры.УстановитьЗначение("Номенклатура", ТекущаяНоменклатура);
    Элементы.СписокПриходов.Обновить(); // Обновляем данные в динамическом списке
КонецПроцедуры

Этот способ позволяет избежать передачи больших объемов данных через серверные вызовы, так как динамический список сам заботится о выборке данных.

Оптимизация производительности: Управление серверными вызовами

Когда мы используем событие ПриАктивизацииСтроки для вызова серверной процедуры, есть риск частых и избыточных серверных обращений, особенно если пользователь быстро перемещается по списку. Это может негативно сказаться на производительности. Давайте выясним причину и разберем способы ее устранения.

Проблема частых серверных вызовов

Если пользователь быстро прокручивает список или перемещается по нему стрелками клавиатуры, событие ПриАктивизацииСтроки срабатывает для каждой новой активной строки. Это означает, что для каждой строки будет выполнен отдельный серверный вызов, что может замедлить работу приложения.

Решение: Подключение обработчика ожидания

Для предотвращения избыточных серверных вызовов мы можем использовать механизм обработчика ожидания. Это позволяет отложить выполнение серверной процедуры до того момента, когда пользователь "успокоится" и перестанет активно взаимодействовать со списком.

Метод ПодключитьОбработчикОжидания() объекта ФормаКлиентскогоПриложения позволяет нам указать процедуру, которая будет вызываться в "состоянии покоя" системы (когда нет активных действий пользователя) по истечении заданного интервала времени.

Пример использования ПодключитьОбработчикОжидания


// В модуле формы справочника Номенклатура

// Переменная для хранения текущей номенклатуры, для которой нужно обновить данные
Перем г_ТекущаяНоменклатураДляОбновленияНаСервере Экспорт;

&НаКлиенте
Процедура СписокПриАктивизацииСтроки(Элемент)
    ТекущаяНоменклатура = Элементы.Список.ТекущиеДанные.Ссылка;

    // Сохраняем ссылку на номенклатуру, для которой требуется обновить данные
    г_ТекущаяНоменклатураДляОбновленияНаСервере = ТекущаяНоменклатура;

    // Подключаем обработчик ожидания.
    // Если он уже подключен, система просто обновит его параметры (если они изменились),
    // или сбросит таймер, если вызов происходит до истечения интервала.
    ПодключитьОбработчикОжидания("ОбработатьАктивизациюСтрокиСЗадержкой", 0.5, Истина); // 0.5 секунды, однократно
КонецПроцедуры

&НаКлиенте
Процедура ОбработатьАктивизациюСтрокиСЗадержкой()
    // Этот код выполнится только после паузы в 0.5 секунды
    // с момента последнего вызова СписокПриАктивизацииСтроки

    Если г_ТекущаяНоменклатураДляОбновленияНаСервере <> Неопределено Тогда
        // Вызываем серверную процедуру
        ПолучениеДанныхНоменклатурыНаСервере(г_ТекущаяНоменклатураДляОбновленияНаСервере);
        г_ТекущаяНоменклатураДляОбновленияНаСервере = Неопределено; // Сбрасываем значение
    КонецЕсли;
КонецПроцедуры

// Серверная процедура ПолучениеДанныхНоменклатурыНаСервере остается без изменений

В этом примере:

  1. При каждом срабатывании СписокПриАктивизацииСтроки мы сохраняем ссылку на текущую номенклатуру и подключаем обработчик ожидания.
  2. Параметр 0.5 означает, что процедура ОбработатьАктивизациюСтрокиСЗадержкой будет вызвана через 0.5 секунды.
  3. Параметр Истина означает, что обработчик будет вызван однократно после истечения интервала и затем автоматически отключится. Если пользователь снова активизирует строку до истечения 0.5 секунды, таймер сбросится, и отсчет начнется заново. Таким образом, серверный вызов произойдет только после того, как пользователь остановится на какой-либо строке на полсекунды.

Альтернатива: Заполнение по команде

Если интерактивность не является критичной, мы можем полностью отказаться от автоматического обновления по активации строки. Вместо этого, разместим на форме кнопку "Показать детали" или "Обновить". Пользователь будет выбирать строку, а затем нажимать кнопку для получения подробной информации. Это гарантированно минимизирует серверные вызовы, но снижает интерактивность интерфейса.

Заключение

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

← К списку