Как определить, что строка таблицы значений была изменена в 1С

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

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

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

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

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


Таблица = Новый ТаблицаЗначений;
Таблица.Колонки.Добавить("Поле", Новый ОписаниеТипов("Число"));
Строка = Таблица.Добавить();
Строка.Поле = 1;
Сообщить(ЗначениеВСтрокуВнутр(Строка)); // Выведет внутренний ID

Строка.Поле = 2;
Сообщить(ЗначениеВСтрокуВнутр(Строка)); // Выведет ТОТ ЖЕ САМЫЙ внутренний ID

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

Решение 1: Использование служебной колонки «Изменено»

Это наиболее простой и надежный метод, который выбрал автор темы. Разберем его по шагам. Мы добавляем в таблицу значений (или в реквизит формы типа ДанныеФормыКоллекция) дополнительную колонку Изменено с типом Булево.

  1. Добавим колонку в таблицу (программно или в редакторе формы).
  2. Для каждого элемента управления (поля ввода) в колонках таблицы назначим обработчик события ПриИзменении.
  3. В обработчике будем устанавливать флаг модифицированности.

Пример реализации обработчика:


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

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

Решение 2: Сравнение «слепков» строки через структуры

Если мы не хотим добавлять лишние колонки в данные, проанализируем ситуацию с использованием временных хранилищ или клиентских переменных. Мы можем «снимать слепок» строки в момент начала редактирования и сравнивать его в момент окончания.

Для этого лучше всего подходит объект Структура. Рассмотрим алгоритм:

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

&НаКлиенте
Перем ДанныеСтрокиДоИзменения;

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

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

Решение 3: Сериализация в JSON для универсального сравнения

Для более современных версий платформы (8.3) мы можем использовать преобразование в JSON. Это позволяет получить реальное строковое представление данных, которое будет меняться при изменении значений ячеек, в отличие от ЗначениеВСтрокуВнутр.

Выясним, как это работает: мы сериализуем строку (предварительно преобразовав её в структуру или соответствие) в формат JSON. Полученная строка и будет нашим «хешем» для сравнения.


// Пример функции для получения "слепка" строки
&НаКлиенте
Функция ПолучитьХешСтроки(ДанныеСтроки)
    ЗаписьJSON = Новый ЗаписьJSON;
    ЗаписьJSON.УстановитьСтроку();
    
    // Преобразуем ДанныеФормыСтруктура в обычную структуру для сериализации
    Структ = Новый Структура;
    // Перечисляем только нужные значимые колонки
    Структ.Вставить("Количество", ДанныеСтроки.Количество);
    Структ.Вставить("Цена", ДанныеСтроки.Цена);
    
    ЗаписатьJSON(ЗаписьJSON, Структ);
    Возврат ЗаписьJSON.Закрыть();
КонецФункции

Нюансы работы с типами данных

При ручном сравнении значений (через <>) следует быть осторожными с типами Неопределено, NULL и пустыми ссылками. В 1С сравнение Неопределено и пустой ссылки справочника вернет Ложь (они не равны), что может быть расценено как изменение, хотя фактически поле осталось "пустым" для пользователя.

Также проанализируем ситуацию с добавлением и удалением строк:

Использование идентификаторов в Управляемых формах

Помните, что в управляемом приложении таблица на форме — это объект типа ДанныеФормыКоллекция. У каждой строки такой коллекции есть уникальный идентификатор, который можно получить методом ПолучитьИдентификатор(). Используйте его для хранения "слепков" данных в соответствии (объект Соответствие), где ключом будет ID строки, а значением — структура её данных до изменения. Это позволит вам эффективно проверять модифицированность любой строки в любой момент времени, не привязываясь только к событиям окончания редактирования.

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

← На главную