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

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

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

Метод 1. Создание «снимка данных» (Snapshot) через Соответствие

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

Проанализируем пошагово реализацию этого подхода:

  1. Создадим реквизит формы с именем КэшЗначений и типом Произвольный.
  2. В событии ПриСозданииНаСервере или ПриЧтенииНаСервере заполним этот кэш текущими данными.

Рассмотрим пример кода для инициализации кэша:


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

Теперь в клиентском событии ПриИзменении мы можем легко получить предыдущее значение и сравнить его с новым:


&НаКлиенте
Процедура ИмяПоляПриИзменении(Элемент)
    ИмяРеквизита = Элемент.Имя;
    НовоеЗначение = Объект[ИмяРеквизита];
    СтароеЗначение = КэшЗначений.Получить(ИмяРеквизита);
    
    Если НовоеЗначение <> СтароеЗначение Тогда
        // Выполняем необходимую логику
        Сообщить("Значение изменилось с " + СтароеЗначение + " на " + НовоеЗначение);
        
        // Не забываем обновить кэш после обработки
        КэшЗначений.Вставить(ИмяРеквизита, НовоеЗначение);
    КонецЕсли;
КонецПроцедуры

Метод 2. Использование события ОкончаниеВводаТекста

Выясним причину, по которой иногда ПриИзменении оказывается не самым удобным событием. Если нам нужно перехватить значение именно в текстовом поле до того, как оно будет окончательно записано в реквизит формы, мы можем использовать событие ОкончаниеВводаТекста. Это событие вызывается, когда пользователь закончил набор, но фокус еще не покинул поле.

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


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

Метод 3. Сравнение с данными в базе данных

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

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


&НаСервере
Функция ПолучитьСтароеЗначениеИзБазы(СсылкаОбъекта, ИмяПоля)
    Возврат СсылкаОбъекта[ИмяПоля];
КонецФункции

&НаКлиенте
Процедура ИмяПоляПриИзменении(Элемент)
    СтароеЗначение = ПолучитьСтароеЗначениеИзБазы(Объект.Ссылка, "ИмяРеквизита");
    // Сравниваем с Объект.ИмяРеквизита
КонецПроцедуры

Метод 4. Работа с табличными частями

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

Проанализируем этот сценарий:


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

&НаКлиенте
Процедура ТабличнаяЧастьПриАктивизацииСтроки(Элемент)
    ТекущиеДанные = Элемент.ТекущиеДанные;
    Если ТекущиеДанные <> Неопределено Тогда
        КэшТекущейСтроки = Новый Структура;
        КэшТекущейСтроки.Вставить("Количество", ТекущиеДанные.Количество);
        КэшТекущейСтроки.Вставить("Цена", ТекущиеДанные.Цена);
    КонецЕсли;
КонецПроцедуры

&НаКлиенте
Процедура ТабличнаяЧастьКоличествоПриИзменении(Элемент)
    ТекущиеДанные = Элементы.ТабличнаяЧасть.ТекущиеДанные;
    СтароеЗначение = КэшТекущейСтроки.Количество;
    
    // Логика сравнения...
КонецПроцедуры

Особенности программного изменения значений

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

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

Резюме

Несмотря на отсутствие штатного параметра СтароеЗначение, мы имеем широкий арсенал инструментов для решения задачи:

  1. Для простых форм: хранение значения в переменной модуля формы.
  2. Для сложных многопоточных интерфейсов: использование Соответствия для создания "снимка" всех реквизитов.
  3. Для контроля ввода текста: использование события ОкончаниеВводаТекста.
  4. Для контроля данных в таблицах: кэширование при активации строки.

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

← На главную