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