Как определить, какой реквизит изменен: шапки или табличной части, и настроить единый обработчик в 1С?

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

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

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

Рассмотрим несколько надежных и эффективных способов решения этой задачи на управляемых формах 1С.

Способ 1. Рекурсивный обход родителей элемента на клиенте (Рекомендуемый метод)

Иногда разработчики пытаются определить принадлежность элемента к табличной части по его имени (например, анализируя наличие префиксов или вхождение имени табличной части в имя элемента формы). Однако этот способ крайне ненадежен: имена элементов могут быть переименованы вручную в конфигураторе или изменены программно в процессе работы формы. Это может привести к сбою логики.

Самым надежным, быстрым и правильным способом определить на стороне клиента, относится ли изменяемое поле к табличной части, является анализ коллекции родительских элементов элемента формы. У каждого элемента управления на форме существует свойство Родитель. Если элемент вложен в таблицу формы, то один из его родителей на верхних уровнях иерархии обязательно будет иметь тип ТаблицаФормы.

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


&НаКлиенте
Процедура ОбщийОбработчикПриИзменении(Элемент)
    
    ИмяТаблицы = "";
    ТекущийРодитель = Элемент.Родитель;
    
    // Запускаем цикл обхода вверх по дереву элементов формы
    Пока ТекущийРодитель <> Неопределено Цикл
        Если ТипЗнч(ТекущийРодитель) = Тип("ТаблицаФормы") Тогда
            ИмяТаблицы = ТекущийРодитель.Имя; // Мы нашли таблицу-контейнер!
            Прервать;
        КонецЕсли;
        ТекущийРодитель = ТекущийРодитель.Родитель;
    КонецЦикла;
    
    // Разделяем логику обработки на основе полученных данных
    Если ЗначениеЗаполнено(ИмяТаблицы) Тогда
        // Реквизит принадлежит табличной части
        // Мы можем сразу получить доступ к данным редактируемой строки:
        ТекущаяСтрока = Элементы[ИмяТаблицы].ТекущиеДанные;
        
        Если ТекущаяСтрока <> Неопределено Тогда
            ПоказатьОповещениеПользователя("Изменение в ТЧ",, "Изменена колонка: " + Элемент.Имя + " в строке таблицы: " + ИмяТаблицы);
            // Здесь выполняем логику для табличной части, используя ТекущаяСтрока
        КонецЕсли;
    Иначе
        // Реквизит принадлежит непосредственно шапке документа (или реквизитам самой формы)
        ПоказатьОповещениеПользователя("Изменение в шапке",, "Изменен реквизит шапки: " + Элемент.Имя);
        // Здесь выполняем логику для шапки документа
    EndIf;
    
КонецПроцедуры

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

Способ 2. Анализ свойства «ПутьКДанным»

Каждое поле ввода на форме жестко связано с конкретным реквизитом объекта или формы через свойство ПутьКДанным (DataPath). Анализ этого пути позволяет безошибочно определить структуру хранения изменяемой информации.

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

  1. На клиенте срабатывает единый обработчик ПриИзменении(Элемент).
  2. Имя элемента формы передается в серверную процедуру в качестве параметра.
  3. На сервере по имени элемента из коллекции Элементы получается объект управления и анализируется его путь к данным.

Давайте проанализируем пример реализации такого подхода:


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

&НаСервере
Процедура АнализироватьИзменениеНаСервере(ИмяЭлемента)
    
    ЭлементФормы = Элементы[ИмяЭлемента];
    ПутьКДанным = ЭлементФормы.ПутьКДанным; // Получаем строку, например, "Объект.Товары.ТекущиеДанные.Номенклатура"
    
    // Разбираем путь к данным по разделителю "точка"
    МассивЧастей = СтрРазделить(ПутьКДанным, ".");
    
    Если МассивЧастей.Количество() > 2 И МассивЧастей[0] = "Объект" Тогда
        
        // Проверяем, является ли второй элемент имени табличной частью объекта
        ИмяВозможнойТЧ = МассивЧастей[1];
        МетаданныеДокумента = Объект.Метаданные();
        
        Если МетаданныеДокумента.ТабличныеЧасти.Найти(ИмяВозможнойТЧ) <> Неопределено Тогда
            // Это гарантированно реквизит табличной части!
            ИмяРеквизитаТЧ = МассивЧастей[МассивЧастей.ВГраница()];
            // Выполняем серверную бизнес-логику для ТЧ
        Иначе
            // Это реквизит шапки объекта
            ИмяРеквизитаШапки = МассивЧастей[1];
            // Выполняем серверную бизнес-логику для шапки
        КонецЕсли;
        
    КонецЕсли;

КонецПроцедуры

Способ 3. Прямая работа с метаданными объекта

Среди начинающих разработчиков бытует мнение, что обращение к метаданным в 1С (к методу Объект.Метаданные()) может вызывать существенные задержки ("тормоза") в работе интерфейса. Это миф.

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

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


&НаСервере
Функция ОпределитьПринадлежностьПоМетаданным(ИмяРеквизита)
    
    МетаданныеОбъекта = Объект.Метаданные();
    
    // Обходим все табличные части документа
    Для Каждого ТабличнаяЧасть Из МетаданныеОбъекта.ТабличныеЧасти Цикл
        // Пытаемся найти реквизит внутри конкретной ТЧ
        Если ТабличнаяЧасть.Реквизиты.Найти(ИмяРеквизита) <> Неопределено Тогда
            Возврат "ТабличнаяЧасть_" + ТабличнаяЧасть.Имя;
        КонецЕсли;
    КонецЦикла;
    
    // Если в ТЧ не найдено, проверяем реквизиты шапки
    Если МетаданныеОбъекта.Реквизиты.Найти(ИмяРеквизита) <> Неопределено Тогда
        Возврат "Шапка";
    КонецЕсли;
    
    Возврат "НеНайдено";
    
КонецФункции

Как получить идентификатор (ссылку) документа при изменении

В управляемом приложении модуль формы всегда имеет постоянный контекстный доступ к основному реквизиту формы, обычно это Объект (содержащий тип ДокументОбъект.ИмяДокумента).

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

Резюме и лучшие практики реализации

Для построения качественной, расширяемой архитектуры вашей формы придерживайтесь следующих правил:

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