При разработке интерфейсов в 1С часто возникает задача отобразить связанные данные: например, в верхней части формы расположен список заказов клиентов, а в нижней — таблица с товарами выбранного заказа. Нередко после добавления второго динамического списка форма начинает открываться со значительной задержкой, даже если панель с товарами по умолчанию скрыта. Разберем причины такого поведения платформы и подробно рассмотрим проверенные методы оптимизации.
Проанализируем механизм работы платформы при открытии управляемой формы. Распространенное заблуждение заключается в том, что свойство Видимость = Ложь предотвращает обращение к данным. На практике происходят следующие процессы:
Видимость управляет только отображением элементов на стороне клиента. На сервере при вызове события ПриСозданииНаСервере платформа в любом случае инициализирует схему динамического списка, подготавливает внутренние структуры данных и выполняет первичный запрос к базе данных для получения первой страницы строк.Документ.ЗаказКлиента.Товары) невозможно указать свойство ОсновнаяТаблица. Из-за этого отключается механизм динамического считывания порциями по кластерному индексу, а СУБД выполняет запрос как для произвольной неиндексированной выборки.Если второй список по умолчанию скрыт и включается пользователем по флагу, наиболее быстрый способ избежать лишней нагрузки при открытии — установить в качестве текста запроса легковесную заглушку в серверном обработчике формы. Выясним, как это реализовать.
При создании формы на сервере заменим сложный запрос динамического списка на минимальный, а боевой текст запроса сохраним или восстановим только в момент включения видимости:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Если по умолчанию панель состава выключена, отключаем тяжелый запрос
Если Не Элементы.ГруппаСоставЗаказа.Видимость Тогда
СписокТовары.ТекстЗапроса = "ВЫБРАТЬ 1 КАК Поле";
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ФлагПоказыватьСоставПриИзменении(Элемент)
Элементы.ГруппаСоставЗаказа.Видимость = ФлагПоказыватьСостав;
ВключитьОтображениеСоставаНаСервере(ФлагПоказыватьСостав);
КонецПроцедуры
&НаСервере
Процедура ВключитьОтображениеСоставаНаСервере(Включить)
Если Включить Тогда
СписокТовары.ТекстЗапроса =
"ВЫБРАТЬ
| ЗаказТовары.Номенклатура КАК Номенклатура,
| ЗаказТовары.Количество КАК Количество,
| ЗаказТовары.ВариантОбеспечения КАК ВариантОбеспечения
|ИЗ
| Документ.ЗаказКлиента.Товары КАК ЗаказТовары
|ГДЕ
| ЗаказТовары.Ссылка = &ТекЗаказ";
// Устанавливаем актуальный параметр текущей строки
ТекущиеДанные = Элементы.СписокЗаказы.ТекущиеДанные;
ТекСсылка = ?(ТекущиеДанные <> Неопределено, ТекущиеДанные.Ссылка, ПредопределенноеЗначение("Документ.ЗаказКлиента.ПустаяСсылка"));
СписокТовары.Параметры.УстановитьЗначениеПараметра("ТекЗаказ", ТекСсылка);
Иначе
СписокТовары.ТекстЗапроса = "ВЫБРАТЬ 1 КАК Поле";
КонецЕсли;
КонецПроцедуры
Для отображения подчиненных строк документа использование второго динамического списка часто оказывается избыточным. Более эффективное архитектурное решение — использовать обычную таблицу формы (коллекцию ТаблицаЗначений или ДанныеФормыКоллекция), которая заполняется по требованию.
Чтобы избежать многократных серверных вызовов, когда пользователь быстро перемещается по основному списку с зажатой клавишей со стрелкой, обязательно реализуем развязку через клиентский таймер (debounce):
&НаКлиенте
Процедура СписокЗаказыПриАктивизацииСтроки(Элемент)
// Перезапускаем однократный таймер с задержкой 0.1-0.2 секунды
ОтключитьОбработчикОжидания("ОбновитьСоставТекущегоЗаказа");
ПодключитьОбработчикОжидания("ОбновитьСоставТекущегоЗаказа", 0.15, Истина);
КонецПроцедуры
&НаКлиенте
Процедура ОбновитьСоставТекущегоЗаказа()
ТекущиеДанные = Элементы.СписокЗаказы.ТекущиеДанные;
Если ТекущиеДанные = Неопределено Или Не Элементы.ГруппаСоставЗаказа.Видимость Тогда
ТоварыТекущегоЗаказа.Очистить();
Возврат;
КонецЕсли;
ЗаполнитьСоставНаСервере(ТекущиеДанные.Ссылка);
КонецПроцедуры
&НаСервере
Процедура ЗаполнитьСоставНаСервере(СсылкаНаЗаказ)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ЗаказТовары.Номенклатура КАК Номенклатура,
| ЗаказТовары.Количество КАК Количество,
| ЗаказТовары.ВариантОбеспечения КАК ВариантОбеспечения
|ИЗ
| Документ.ЗаказКлиента.Товары КАК ЗаказТовары
|ГДЕ
| ЗаказТовары.Ссылка = &СсылкаНаЗаказ";
Запрос.УстановитьПараметр("СсылкаНаЗаказ", СсылкаНаЗаказ);
// Загружаем результат напрямую в реквизит формы
ТоварыТекущегоЗаказа.Загрузить(Запрос.Выполнить().Выгрузить());
КонецПроцедуры
Преимущество такого подхода: при быстром пролистывании основного списка запросы к базе данных выполняются только тогда, когда пользователь остановился на конкретной строке.
Если для бизнес-логики требуется выводить актуальные состояния обеспечения, остатки к отгрузке или резервы, выборка напрямую из табличной части документа может быть неоптимальной и неполной. Рассмотрим рекомендации по работе с источниками:
РегистрНакопления.ОбеспечениеЗаказов или РегистрНакопления.ЗаказыКлиентов). Чтение срезов или оборотов из индексированных таблиц регистров по конкретному заказу выполняется быстрее, чем сканирование строк документа.ГДЕ Таблица.Ссылка = &ТекЗаказ внутри текста запроса. Рекомендуется настраивать отбор через Список.КомпоновщикНастроек.Настройки.Отбор. Когда список неактивен, элемент отбора можно программно отключать (Использование = Ложь), что позволяет оптимизатору платформы эффективнее кэшировать планы выполнения запросов.Еще один способ ускорить инициализацию — отключить привязку таблицы формы к источнику на этапе создания. Если у элемента таблицы формы свойство ПутьКДанным не заполнено, платформа не опрашивает динамический список при открытии формы.
При включении видимости пользователем мы программно устанавливаем связь:
&НаСервере
Процедура ПереключитьСвязьТаблицы(Включить)
Если Включить Тогда
Элементы.ТаблицаТовары.ПутьКДанным = "СписокТовары";
Иначе
Элементы.ТаблицаТовары.ПутьКДанным = "";
КонецЕсли;
КонецПроцедуры
Применение этих методов позволяет свести время открытия формы списка документов к минимуму, сохранив при этом удобный интерактивный просмотр состава заказов для пользователя.