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

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

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

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

Механизм работы события ПриИзменении для составных типов

Для начала рассмотрим, как платформа 1С версии 8.3 должна обрабатывать интерактивное изменение поля с составным типом. По стандартам управляемого интерфейса, это действие является двухэтапным:

  1. Выбор типа: Пользователь нажимает на кнопку выбора, открывается список доступных типов (например, СправочникСсылка.Контрагенты и СправочникСсылка.ФизическиеЛица). После выбора типа значение в поле меняется с Неопределено на пустую ссылку выбранного типа. В этот момент событие ПриИзменении должно вызваться первый раз.
  2. Выбор значения: Пользователь выбирает конкретный элемент из справочника. Значение в поле снова меняется. В этот момент событие ПриИзменении должно вызваться второй раз.

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

Влияние метода ЗначениеВРеквизитФормы на логику событий

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

Разберем по шагам, что происходит в памяти системы:

  1. Пользователь выбирает тип данных в ячейке таблицы.
  2. Срабатывает клиентский обработчик ПриИзменении, который передает управление на сервер для выполнения расчетов.
  3. На сервере разработчик вызывает ЗначениеВРеквизитФормы(ОбъектДанных, "Объект"). Этот метод полностью пересоздает программные объекты на форме, синхронизируя их с данными из прикладного объекта.
  4. В результате такой «жесткой» синхронизации состояние элементов управления на клиенте сбрасывается. Платформа «теряет» контекст того, что в данный момент ячейка находится в состоянии редактирования и ожидает второго этапа (выбора значения).

Посмотрим на пример кода, который приводит к ошибке:


&НаКлиенте
Процедура ТЧПриИзменении(Элемент)
    // Вызов серверной процедуры для пересчета
    ПересчитатьДанныеНаСервере();
КонецПроцедуры

&НаСервере
Процедура ПересчитатьДанныеНаСервере()
    // Преобразование реквизита формы в прикладной объект (например, ДокументОбъект)
    ДокОбъект = РеквизитФормыВЗначение("Объект");
    
    // Какие-то расчеты...
    ДокОбъект.РассчитатьИтоги();
    
    // КРИТИЧЕСКАЯ ОШИБКА: Полное обновление данных формы
    ЗначениеВРеквизитФормы(ДокОбъект, "Объект");
КонецПроцедуры

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

Как правильно обновлять данные в обработчиках

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

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


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

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

&НаСервере
Процедура РассчитатьСпецификуНаСервере(НомерСтроки)
    // Работаем напрямую с реквизитом формы (ДанныеФормыКоллекция)
    СтрокаТЧ = Объект.ТЧ.НайтиПоИдентификатору(НомерСтроки - 1); 
    // Или используем другие способы позиционирования
    
    Если СтрокаТЧ <> Неопределено Тогда
        // Обновляем только нужные поля
        СтрокаТЧ.Сумма = СтрокаТЧ.Количество * СтрокаТЧ.Цена;
    КонецЕсли;
    
    // Метод ЗначениеВРеквизитФормы() ЗДЕСЬ НЕ НУЖЕН!
КонецПроцедуры

Рекомендации по оптимизации

Проанализируем дополнительные аспекты, которые помогут сделать работу с таблицами более стабильной:

  1. Проверка на пустое значение: Всегда добавляйте проверку в начале обработчика. Если тип выбран, но само значение еще пустое (ссылка Пустая()), возможно, стоит отложить выполнение основной логики до второго вызова события.
  2. Использование параметров ЗначениеВРеквизитФормы: Если вам все же необходимо использовать этот метод, помните, что у него есть второй параметр, позволяющий обновить не весь объект целиком, а только конкретный путь к данным. Однако в обработчиках изменения ячеек этого все равно лучше избегать.
  3. Версия платформы: Хотя в релизе 8.3.15.1830 данное поведение чаще вызвано кодом, стоит помнить, что в 8.3.16.1063 и выше работа с составными типами стала более стабильной. Если ваш код написан корректно, а событие всё равно не срабатывает — планируйте обновление платформы.
  4. Свойство «Быстрый выбор»: Обратите внимание на это свойство у реквизитов. При включенном быстром выборе поведение платформы при подборе значений может меняться, так как система пытается минимизировать количество кликов пользователя, что иногда схлопывает два вызова в один.

Таким образом, мы выяснили, что странное поведение обработчика ПриИзменении — это чаще всего следствие нарушения контекста выполнения управляемой формы. Избегайте деструктивных методов обновления данных (таких как полная перезагрузка объекта через ЗначениеВРеквизитФормы) в момент интерактивного взаимодействия пользователя с элементами таблицы, и ваши формы будут работать предсказуемо и быстро.

← На главную