Как применить одну функцию «ПриИзменении» для целой группы реквизитов таблицы в 1С 8.3

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

При проектировании сложных интерфейсов в управляемом приложении «1С:Предприятие 8.3» разработчики часто сталкиваются с ситуацией, когда в табличной части или в таблице формы присутствует большое количество однотипных колонок. Классический пример — таблица для ввода спецификаций, характеристик или матрица цветов (например, «Цвет1», «Цвет2», ... «Цвет30»).

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

В этой статье мы подробно разберем, как решить эту задачу в соответствии со стандартами разработки фирмы «1С». Мы научимся программно назначать один общий обработчик для всей группы реквизитов и динамически определять, в какой именно колонке произошло событие изменения.

В чем заключается основная сложность?

Главный вопрос, который возникает у начинающих разработчиков: если мы назначим один и тот же обработчик для тридцати колонок, то как внутри этого обработчика понять, какое именно поле изменил пользователь? Как обратиться к значению конкретной ячейки текущей строки, не прописывая жестко имена колонок в условиях?

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

Рассмотрим весь процесс по шагам.

Шаг 1. Программная привязка единого обработчика событий

Вместо того чтобы вручную открывать свойства каждой колонки в конфигураторе и выбирать процедуру в поле «ПриИзменении», мы напишем код, который сделает это автоматически при открытии формы. Для этого используется метод управляемой формы УстановитьДействие.

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

Существует два основных способа обойти колонки для назначения обработчика. Рассмотрим оба варианта.

Вариант А. Назначение через обход элементов родительской группы (наиболее надежный способ)

Если ваши колонки (например, все «Цвета») объединены на форме в группу колонок (например, с именем ГруппаЦвета), мы можем просто перебрать все подчиненные элементы этой группы.


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

Вариант Б. Назначение по маске имени элемента

Если колонки расположены в таблице свободно и не объединены в отдельную группу, мы можем перебрать все колонки таблицы и отфильтровать их по имени (например, выбрав только те, имя которых начинается со слова «Цвет»).


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

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

Шаг 2. Разработка универсального обработчика события

Теперь перейдем к написанию самого обработчика события на клиенте. Нам нужно решить три задачи:

  1. Получить данные строки, в которой пользователь изменил значение.
  2. Определить точное имя измененного реквизита (колонки).
  3. Получить значение измененной ячейки, сравнить его с реквизитом Номенклатура этой же строки и выполнить бизнес-логику.

Посмотрим, как выглядит готовая реализация такого обработчика:


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

&НаКлиенте
Функция ЗначениеЦветаНеКорректно(ЗначениеЦвета, ЗначениеНоменклатуры)
    
    // Здесь располагается ваша прикладная логика сравнения.
    // Например, проверка заполненности или сложное сопоставление свойств.
    Если ЗначениеЦвета = ЗначениеНоменклатуры Тогда
        Возврат Истина;
    КонецЕсли;
    
    Возврат Ложь;
    
КонецФункции

Разбираем детали реализации

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

Использование свойства ТекущиеДанные: Мы обращаемся к данным строки через Элементы.ТаблицаНоменклатуры.ТекущиеДанные. Это объект типа ДанныеФормыСтруктура. Он позволяет читать и записывать значения колонок текущей активной строки на клиенте без обращений к серверу.

Разбор пути к данным через СтрРазделить: Свойство Элемент.ПутьКДанным всегда содержит полный путь до источника данных элемента управления. Разделив эту строку точкой при помощи функции СтрРазделить, мы получаем массив частей пути. Самый последний элемент этого массива (с индексом ВГРАНИЦУ()) — это и есть имя нашей колонки в табличной части (например, "Цвет5").

Квадратные скобки [] для динамического обращения: Вместо написания громоздких конструкций Если... ИначеЕсли..., мы используем обращение к свойству строки по его строковому имени: ТекущиеДанные[ИмяИзмененногоРеквизита]. Это мощный инструмент платформы 1С, который позволяет делать код лаконичным, гибким и универсальным.

Преимущества описанного подхода

Применение данного паттерна программирования в 1С дает ряд весомых преимуществ:

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

← На главную