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