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