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

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке связанных табличных частей по схеме «мастер-деталь» часто возникает задача фильтрации строк подчиненной таблицы при смене текущей строки в главной таблице. Однако нередко разработчики сталкиваются с ситуацией, когда при перемещении курсора форма начинает бесконечно перезагружаться или платформа 1С аварийно завершает работу из-за переполнения стека вызовов.

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

Анализ проблемы и причин бесконечной рекурсии

Рассмотрим типовой сценарий: на управляемой форме расположены две таблицы — главная (например, Условия) и подчиненная (Товары). Связь между ними осуществляется по идентификатору УидСтрокиОУ.

Посмотрим на ошибочный пример кода, приводящий к зацикливанию:


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

&НаСервере
Процедура УсловияПриАктивизацииСтрокиНаСервере(Знач УидСтрокиОУ)
    // Ошибочный контекстный серверный вызов для установки клиентского свойства
    Элементы.Товары.ОтборСтрок.УидСтрокиОУ.Установить(УидСтрокиОУ);
КонецПроцедуры

Выясним причину, по которой данный код вызывает зацикливание платформы:

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

Способ 1. Установка отбора на клиенте без обращения к серверу

Свойство таблицы формы ОтборСтрок доступно на клиенте. Для фильтрации строк таблицы нет никакой необходимости обращаться к серверу. Фильтрация задается присвоением объекту ОтборСтрок новой фиксированной структуры.

Разберем по шагам правильную реализацию на клиенте:


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

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

Способ 2. Защита от повторных срабатываний через кэширование ключа связи

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

Создадим клиентскую переменную формы или реквизит формы (например, ТекущийУидСтрокиОУ) и модифицируем код:


&НаКлиенте
Перем ТекущийУидСтрокиОУ;

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

Этот прием полностью исключает повторное применение отбора, если пользователь просто кликает по разным колонкам одной и той же строки.

Способ 3. Использование флага блокировки от взаимной рекурсии

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

Рассмотрим пример реализации с защитой через флаг:


&НаКлиенте
Перем БлокировкаАктивизации;

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

Способ 4. Вызов сервера через обработчик ожидания (если сервер действительно необходим)

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

Посмотрим на пример правильной организации отложенного вызова:


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

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

&НаСервереБезКонтекста
Процедура ОбработатьДанныеНаСервере(Знач УидСтрокиОУ)
    // Серверная логика без передачи контекста формы
КонецПроцедуры

Рекомендации и правила работы с событием ПриАктивизацииСтроки

  1. Никогда не используйте контекстные методы &НаСервере: Это прямое требование стандартов разработки 1С.
  2. Свойство ОтборСтрок работает на клиенте: Для установки фильтра всегда передавайте экземпляр ФиксированнаяСтруктура непосредственно на клиенте.
  3. Для полного сброса отбора: Присваивайте свойству ОтборСтрок значение Неопределено или пустую структуру.
  4. Оптимизируйте навигацию: Применяйте проверку изменения активной строки, чтобы исключить повторную инициализацию интерфейса при перемещении по колонкам одной строки.
← На главную