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