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