Почему событие ПриАктивизацииСтроки срабатывает многократно и как правильно рассчитывать остатки в форме выбора

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

При разработке управляемых форм в 1С часто возникает задача динамического отображения сопутствующей информации (например, текущих остатков и цен номенклатуры) при перемещении пользователя по строкам таблицы или динамического списка. Разработчики традиционно используют для этого событие ПриАктивизацииСтроки. Однако при отладке выясняется, что данный обработчик вызывается неожиданно много раз: как при открытии формы, так и при обычном клике мышью на строку списка. Выясним причину такого поведения платформы и рассмотрим правильные варианты реализации данной задачи.

Причины многократного вызова события ПриАктивизацииСтроки

Проанализируем механику работы управляемой формы и динамического списка, которая приводит к серии вызовов обработчика:

  1. Инициализация и динамическая компоновка формы: при открытии формы выбора платформа выполняет начальную отрисовку таблицы, порционную загрузку порций данных и расчет условного оформления. В процессе позиционирования фокуса и установки первоначальной строки платформа может программно инициировать смену активной строки несколько раз подряд.
  2. Навигация и прокрутка: когда пользователь перемещается по списку стрелками клавиатуры или колесом мыши, событие ПриАктивизацииСтроки генерируется для каждого промежуточного элемента. Если на каждое срабатывание повесить прямой серверный вызов, интерфейс начинает подвисать из-за очереди запросов.
  3. Каскадные серверные вызовы: если внутри обработчика выполняется контекстный вызов сервера, модифицирующий реквизиты формы, платформа после возврата на клиент выполняет синхронизацию состояния и повторно перерисовывает элементы, что провоцирует новый виток вызова событий строки.

Способ 1. Использование однократного обработчика ожидания (паттерн Debounce)

Наиболее надежный и рекомендуемый способ обработки частых клиентских событий — это использование отложенного вызова с помощью методов ПодключитьОбработчикОжидания и ОтключитьОбработчикОжидания. Суть метода заключается в том, чтобы не отправлять запрос на сервер сразу, а подождать короткую паузу (обычно 0.1–0.3 секунды). Если за это время строка снова изменилась, таймер сбрасывается и запускается заново.

Разберем по шагам реализацию этого механизма в модуле управляемой формы:


&НаКлиенте
Процедура СписокПриАктивизацииСтроки(Элемент)
    // Снимаем предыдущий отложенный вызов, если пользователь продолжает перемещаться
    ОтключитьОбработчикОжидания("ОбновитьОстаткиПоВыбраннойНоменклатуре");
    
    // Подключаем однократный вызов с минимальной задержкой
    ПодключитьОбработчикОжидания("ОбновитьОстаткиПоВыбраннойНоменклатуре", 0.2, Истина);
КонецПроцедуры

&НаКлиенте
Процедура ОбновитьОстаткиПоВыбраннойНоменклатуре()
    ТекущиеДанные = Элементы.Список.ТекущиеДанные;
    
    Если ТекущиеДанные = Неопределено Тогда
        ОстатокНаСкладе = 0;
        Возврат;
    КонецЕсли;
    
    // Выполняем серверный вызов только после фиксации пользователя на строке
    ОстатокНаСкладе = ПолучитьОстатокНоменклатурыНаСервере(ТекущиеДанные.Ссылка);
КонецПроцедуры

&НаСервереБезКонтекста
Функция ПолучитьОстатокНоменклатурыНаСервере(НоменклатураСсылка)
    // Запрос к виртуальной таблице остатков регистра накопления
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   ТоварыНаСкладахОстатки.ВНаличииОстаток КАК Остаток
        |ИЗ
        |   РегистрНакопления.ТоварыНаСкладах.Остатки(, Номенклатура = &Номенклатура) КАК ТоварыНаСкладахОстатки";
        
    Запрос.УстановитьПараметр("Номенклатура", НоменклатураСсылка);
    Выборка = Запрос.Выполнить().Выбрать();
    
    Если Выборка.Следующий() Тогда
        Возврат Выборка.Остаток;
    КонецЕсли;
    
    Возврат 0;
КонецФункции

В результате такого подхода при быстрой прокрутке списка сервер не нагружается промежуточными запросами, а расчет остатков выполняется ровно один раз — когда пользователь остановил курсор на требуемом товаре.

Способ 2. Проверка факта смены текущего объекта (кэширование)

Рассмотрим ситуацию, когда форма повторно вызывает событие ПриАктивизацииСтроки для одной и той же номенклатуры (например, при клике мышью в разные колонки той же строки). В таком случае достаточно отсечь лишние вызовы простой проверкой через переменную модуля формы.

Посмотрим на пример реализации:


&НаКлиенте
Переменная ТекущаяОбработаннаяНоменклатура;

&НаКлиенте
Процедура СписокПриАктивизацииСтроки(Элемент)
    ТекущиеДанные = Элементы.Список.ТекущиеДанные;
    
    Если ТекущиеДанные = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    // Если строка фактически не изменилась, прерываем выполнение
    Если ТекущаяОбработаннаяНоменклатура = ТекущиеДанные.Ссылка Тогда
        Возврат;
    КонецЕсли;
    
    ТекущаяОбработаннаяНоменклатура = ТекущиеДанные.Ссылка;
    
    // Переходим к получению остатков
    ОтключитьОбработчикОжидания("ОбновитьОстаткиПоВыбраннойНоменклатуре");
    ПодключитьОбработчикОжидания("ОбновитьОстаткиПоВыбраннойНоменклатуре", 0.1, Истина);
КонецПроцедуры

Способ 3. Перенос расчета остатков в запрос динамического списка

Проанализируем альтернативный архитектурный подход: вместо вызова сервера на каждую смену строки можно включить вывод остатков непосредственно в запрос динамического списка формы выбора.

Разберем особенности этого способа:

  1. В запросе динамического списка настраивается левое соединение справочника номенклатуры с виртуальной таблицей остатков регистра (например, РегистрНакопления.ТоварыНаСкладах.Остатки).
  2. Параметры виртуальной таблицы (склад, период) передаются программно через коллекцию Список.Параметры.УстановитьЗначениеПараметра() при открытии формы или смене склада в шапке.
  3. Платформа 1С самостоятельно запрашивает и отображает остатки только для видимой пользователю порции строк на экране.
  4. Событие ПриАктивизацииСтроки в таком сценарии вообще не используется, что полностью устраняет проблему лишних клиент-серверных вызовов и обеспечивает максимальную отзывчивость интерфейса.

Резюме: Если требуется показывать остаток в отдельной информационной панели под списком, оптимальным решением является связка события ПриАктивизацииСтроки и однократного обработчика ожидания ПодключитьОбработчикОжидания. Если же остатки должны быть видны сразу по всем товарам в таблице, расчет целесообразно перенести прямо в текст запроса динамического списка.

← На главную