Как в управляемых формах определить, что строка таблицы была изменена?

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

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

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

Особенности объекта ДанныеФормыЭлементКоллекции

Проанализируем поведение платформы при попытке сериализовать текущую строку таблицы управляемой формы. Строка таблицы на клиенте представляет собой тип ДанныеФормыЭлементКоллекции. Это специальный прокси-объект, ссылающийся на данные формы, а не самостоятельный контейнер по типу СтрокаТаблицыЗначений.

По этой причине стандартные методы сериализации работают специфично:

  1. Метод ЗначениеВСтрокуВнутр для объекта ДанныеФормыЭлементКоллекции возвращает лишь внутреннее системное описание прокси-объекта (ссылку), а не состав его значений.
  2. Если попытаться сохранить строку в переменную модуля формы при событии ПриНачалеРедактирования простым присваиванием, мы получим не снимок значений, а ссылку на ту же самую строку. При завершении редактирования все данные в переменной будут уже измененными, и сравнить их с исходными не удастся.
  3. Методы трансформации всей таблицы вроде ДанныеФормыВЗначение доступны исключительно в контексте сервера, а выполнение серверного вызова на каждый вход и выход из строки создает неоправданную нагрузку на сеть и сервер.

Решение: Снимок строки в структуру на клиенте

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

Разберем по шагам логику работы:

  1. При создании формы получаем перечень колонок таблицы, которые требуется отслеживать.
  2. В обработчике события таблицы ПриНачалеРедактирования проверяем параметр НоваяСтрока. Если строка новая, сразу устанавливаем признак добавления. Если строка существовала ранее — считываем значения всех отслеживаемых полей в буферную переменную формы типа Структура.
  3. В обработчике события ПриОкончанииРедактирования повторно считываем значения колонок и выполняем посимвольное или позначенческое сравнение с сохраненным снимком.
  4. При обнаружении различий взводим статус модификации (например, служебный реквизит КодСостояния), на который можно настроить условное оформление для визуального выделения измененных строк.

Посмотрим на пример реализации данного подхода:


&НаКлиенте
Перем мРедактируемаяСтрока;

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

&НаКлиенте
Функция СериализоватьСтрокуВСтруктуру(СтрДанных)
    Рез = Новый Структура;
    Для Каждого Эл Из КолонкиТаблицы Цикл
        Рез.Вставить(Эл.Значение, СтрДанных[Эл.Значение]);
    КонецЦикла;
    Возврат Рез;
КонецФункции

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

&НаКлиенте
Процедура КонтрагентыПриОкончанииРедактирования(Элемент, НоваяСтрока, ОтменаРедактирования)
    Если ОтменаРедактирования Тогда
        Возврат;
    КонецЕсли;

    Если Не НоваяСтрока И мРедактируемаяСтрока <> Неопределено Тогда
        ТекущееСостояние = СериализоватьСтрокуВСтруктуру(Элементы.Контрагенты.ТекущиеДанные);
        
        Для Каждого КлЗн Из ТекущееСостояние Цикл
            Если мРедактируемаяСтрока[КлЗн.Ключ] <> КлЗн.Значение Тогда
                Элементы.Контрагенты.ТекущиеДанные.КодСостояния = 2; // Строка изменена
                Прервать;
            КонецЕсли;
        КонецЦикла;
    КонецЕсли;
КонецПроцедуры

Оптимизация получения колонок и копирования данных

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

1. Получение состава колонок через элементы формы на клиенте:

Чтобы не задействовать процедуру ПриСозданииНаСервере для сбора имен колонок, можно динамически обойти коллекцию ПодчиненныеЭлементы элемента таблицы прямо на клиенте. У элементов колонок считывается свойство ПутьКДанным, что дает возможность сформировать структуру свойств строки полностью на стороне клиента.

2. Использование метода ЗаполнитьЗначенияСвойств:

Метод ЗаполнитьЗначенияСвойств доступен на тонком клиенте. Если заранее инициализировать структуру с необходимыми ключами, значения из ДанныеФормыЭлементКоллекции можно скопировать одной строкой кода:


// Создаем структуру с именами нужных полей со значениями Неопределено
СтруктураСнимка = Новый Структура("Наименование, ИНН, КПП, Сумма");
ЗаполнитьЗначенияСвойств(СтруктураСнимка, Элементы.Контрагенты.ТекущиеДанные);

Альтернативный вариант: Использование событий элементов ПриИзменении

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


&НаКлиенте
Процедура КолонкаТаблицыПриИзменении(Элемент)
    ТекущиеДанные = Элементы.Контрагенты.ТекущиеДанные;
    Если ТекущиеДанные <> Неопределено И ТекущиеДанные.КодСостояния = 0 Тогда
        ТекущиеДанные.КодСостояния = 2; // Отмечаем как измененную
    КонецЕсли;
КонецПроцедуры

Настройка отображения выделения и условного оформления

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

Для корректного отображения цветов подсветки настроим свойства таблицы формы:

  1. В свойствах таблицы формы находим параметр ОтображениеВыделения.
  2. Устанавливаем значение НеВыделять или Ячейка (в зависимости от требуемого интерфейсного решения), благодаря чему фокус текущей позиции перестает полностью закрашивать настроенные правила условного оформления.
← На главную