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