Как правильно удалить строку из дерева значений в 1С:Предприятие 8

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

Работа с иерархическими структурами данных, такими как Дерево значений, в платформе «1С:Предприятие 8» имеет свои архитектурные особенности, особенно когда речь заходит об управляемых формах. В отличие от обычного списка, удаление строки из дерева требует понимания того, на каком уровне вложенности находится элемент и к какой коллекции он принадлежит. В этой статье мы подробно разберем, как эффективно удалять строки, избегая типичных ошибок производительности.

Анализ проблемы: почему стандартный подход может не работать

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


тДерево = РеквизитФормыВЗначение("Дерево");
тДерево.Строки.Удалить(Номер);
ЗначениеВРеквизитФормы(тДерево, "Дерево");

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

Решение 1: Удаление через идентификатор текущей строки

Самый простой и правильный способ удалить строку, которую пользователь выбрал в интерфейсе — это использование идентификатора. Выясним, как это работает. На клиенте элемент управления «Таблица», отображающая дерево, хранит свойство ТекущаяСтрока. Важно понимать: это не сама строка, а её числовой идентификатор.

Разберем по шагам алгоритм удаления текущей выбранной строки:

  1. Получаем идентификатор текущей строки из элемента формы.
  2. Находим объект строки в данных формы по этому идентификатору.
  3. Определяем родителя этой строки.
  4. Удаляем строку из коллекции родителя (если он есть) или из коллекции верхнего уровня.

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


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

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

КонецПроцедуры

Решение 2: Использование свойства ТекущиеДанные

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


&НаКлиенте
Процедура УдалитьБыстро(Команда)
    
    ДанныеСтроки = Элементы.Дерево.ТекущиеДанные;
    
    Если ДанныеСтроки <> Неопределено Тогда
        Родитель = ДанныеСтроки.ПолучитьРодителя();
        Коллекция = ?(Родитель = Неопределено, Дерево.ПолучитьЭлементы(), Родитель.ПолучитьЭлементы());
        Коллекция.Удалить(ДанныеСтроки);
    КонецЕсли;
    
КонецПроцедуры

Этот метод работает напрямую с типом ДанныеФормыЭлементДерева, что делает код чище и понятнее. Обратите внимание: метод Удалить() у коллекции элементов дерева может принимать как индекс (число), так и саму строку (объект). Это очень удобно.

Важные нюансы: Индекс против Идентификатора

При программной работе с деревьями часто возникает путаница между этими понятиями. Разберем их отличия:

Важный совет: Если вы планируете удалять несколько строк в цикле по условию, всегда используйте обратный порядок обхода. Рассмотрим пример, когда нам нужно удалить все строки, где заполнена колонка ПометкаУдаления:


// Пример для коллекции элементов конкретного уровня
КоллекцияЭлементов = СтрокаРодитель.ПолучитьЭлементы();
Для Счетчик = КоллекцияЭлементов.Количество() - 1 По 0 Цикл
    ТекущаяСтрока = КоллекцияЭлементов.Получить(Счетчик);
    Если ТекущаяСтрока.ПометкаУдаления Тогда
        КоллекцияЭлементов.Удалить(Счетчик);
    КонецЕсли;
КонецЦикла;

Если обходить коллекцию с начала (от 0 до Количество - 1), то после удаления первой же строки индексы всех последующих сместятся, и цикл либо пропустит следующую строку, либо вызовет ошибку выхода за границы массива.

Рекурсивное удаление и полная очистка

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

Рассмотрим пример полной очистки дерева:

Дерево.ПолучитьЭлементы().Очистить();

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

Оптимизация производительности (Server vs Client)

Хотя методы Удалить() и Очистить() доступны и на клиенте, и на сервере, массовые операции с деревом (например, удаление сотен строк после обработки) лучше проводить на сервере. Это связано с тем, что каждое удаление на клиенте может инициировать немедленную перерисовку интерфейса, что замедляет работу. На сервере же вы вносите изменения в данные, которые передаются на клиент один раз по завершении серверного вызова.

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

← На главную