К статье
Практический материал

Разработка и Конфигурация · 12 мин

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

Разбираем, как удалить строку из дерева значений в 1С: метод Удалить(), поиск строк, удаление по условию и очистка ветвей. Примеры кода и типичные ошибки.

Дерево значений в 1С — иерархическая коллекция, где у каждой строки могут быть подчинённые строки. Из-за этой вложенности удаление строки отличается от работы с обычной таблицей значений: нужно учитывать родителя, уровень вложенности и то, что вместе со строкой удаляются все её потомки. Основной инструмент — метод Удалить() коллекции строк дерева.

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

Важно понимать, что метод Удалить() вызывается не у самого дерева, а у коллекции строк — корневой (Дерево.Строки) или у коллекции строк конкретного родителя. Если вызвать удаление не у той коллекции, получите ошибку или удалите не ту строку. Ниже разобраны все рабочие сценарии с примерами кода.

Как устроено удаление строк в дереве значений

Дерево значений состоит из объекта ДеревоЗначений и коллекций СтрокиДереваЗначений на каждом уровне. Каждая строка (СтрокаДереваЗначений) содержит собственную коллекцию Строки с потомками. Удаление возможно только через коллекцию-владельца строки.

У коллекции строк есть три метода, связанных с удалением:

  • Удалить(Строка) — удаляет конкретную строку, переданную как объект или индекс;
  • Очистить() — удаляет все строки данного уровня вместе с их потомками;
  • Сдвинуть() — не удаляет, но часто путается с удалением при ручной реализации перестановки.

Ключевое следствие иерархии: при удалении строки удаляется вся её ветвь целиком. Если у группы было 50 подчинённых элементов, после Удалить() они исчезнут без возможности восстановления. Поэтому перед удалением групповых строк стоит проверять наличие потомков и, при необходимости, запрашивать подтверждение у пользователя.

Удаление текущей строки на форме

Самый частый сценарий — кнопка «Удалить» на форме, где дерево отображается как табличное поле. Для реквизита формы типа «Дерево значений» данные хранятся в объекте ДанныеФормыДерево, и удаление выполняется через текущую строку таблицы формы.

Пример кода команды формы:

&НаКлиенте

Процедура УдалитьСтроку(Команда)

ТекущиеДанные = Элементы.Дерево.ТекущиеДанные;

Если ТекущиеДанные = Неопределено Тогда

Возврат;

КонецЕсли;

УдалитьСтрокуНаСервере(Элементы.Дерево.ТекущаяСтрока);

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

&НаСервере

Процедура УдалитьСтрокуНаСервере(ИдентификаторСтроки)

Дерево = РеквизитФормыВЗначение("ДеревоЗначений");

СтрокаДерева = Дерево.Строки.Найти(ИдентификаторСтроки, "ИдентификаторСтроки", Истина);

// альтернатива — работа через ДанныеФормыДерево напрямую

СтрокаФормы = ДеревоЗначений.НайтиПоИдентификатору(ИдентификаторСтроки);

Родитель = СтрокаФормы.ПолучитьРодителя();

Если Родитель = Неопределено Тогда

ДеревоЗначений.ПолучитьЭлементы().Удалить(СтрокаФормы);

Иначе

Родитель.ПолучитьЭлементы().Удалить(СтрокаФормы);

КонецЕсли;

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

Обратите внимание на проверку родителя. Если ПолучитьРодителя() возвращает Неопределено, строка находится на корневом уровне, и удалять её нужно из коллекции самого дерева. Иначе — из коллекции элементов родителя.

Порядок действий при удалении с формы
  1. 1
    Получите текущую строку табличного поля дерева (Элементы.Дерево.ТекущаяСтрока)
  2. 2
    Проверьте, что строка выбрана — иначе удалять нечего
  3. 3
    Найдите строку в данных дерева по идентификатору (НайтиПоИдентификатору)
  4. 4
    Определите родителя через ПолучитьРодителя()
  5. 5
    Вызовите Удалить() у коллекции элементов дерева или родителя
  6. 6
    Проверьте на форме, что строка и её потомки исчезли

Удаление строки по условию: поиск и удаление

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

Пример удаления строки по значению в колонке:

Процедура УдалитьПоНаименованию(Дерево, ИскомоеНаименование)

НайденныеСтроки = Дерево.Строки.НайтиСтроки(

Новый Структура("Наименование", ИскомоеНаименование), Истина);

Для Каждого Строка Из НайденныеСтроки Цикл

Родитель = Строка.Родитель;

Если Родитель = Неопределено Тогда

Дерево.Строки.Удалить(Строка);

Иначе

Родитель.Строки.Удалить(Строка);

КонецЕсли;

КонецЦикла;

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

Второй параметр Истина в НайтиСтроки() включает поиск по всем уровням иерархии. Без него поиск выполняется только среди корневых строк — это частая причина «неработающего» удаления, когда строка визуально есть, но не находится.

Для произвольного условия удобнее рекурсивный обход с удалением. Важный нюанс: нельзя удалять строки внутри цикла Для Каждого по той же коллекции — это нарушает итерацию. Удаляйте по индексу с конца коллекции к началу:

Процедура УдалитьПустыеГруппы(КоллекцияСтрок)

Для Индекс = 1 - КоллекцияСтрок.Количество() По 0 Цикл

Строка = КоллекцияСтрок[-Индекс];

УдалитьПустыеГруппы(Строка.Строки); // сначала потомки

Если Строка.Строки.Количество() = 0 И Строка.ЭтоГруппа Тогда

КоллекцияСтрок.Удалить(Строка);

КонецЕсли;

КонецЦикла;

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

Удаление всех строк и очистка ветвей

Если нужно удалить все строки дерева, используйте Дерево.Строки.Очистить(). Это быстрее и надёжнее, чем перебор с удалением по одной строке. Для данных формы аналог — ДеревоЗначений.ПолучитьЭлементы().Очистить().

Очистить можно и отдельную ветвь, не удаляя саму строку-родителя: СтрокаГруппы.Строки.Очистить(). Такой приём полезен, например, при перезаполнении группы актуальными данными — структура групп сохраняется, а содержимое обновляется.

Частая ошибка — попытка очистить дерево через удаление строк в цикле Пока Дерево.Строки.Количество() > 0 Цикл. Это работает, но медленнее и создаёт лишний код без выигрыша. Используйте Очистить() всегда, когда нужно удалить всё содержимое уровня.

Сравнение вариантов удаления

Удалить(Строка)

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

Очистить()

Удаление всех строк уровня. Самый быстрый способ полной очистки дерева или ветви. Не подходит для выборочного удаления.

Удаление по индексу с конца

Выборочное удаление в цикле по условию. Безопасно для итерации, но требует аккуратной работы с индексами.

Удаление строк в управляемых формах: особенности ДанныеФормыДерево

В управляемых формах реквизит типа «Дерево значений» представлен объектом ДанныеФормыДерево. Его API отличается от обычного дерева значений: вместо Строки используется ПолучитьЭлементы(), вместо Родитель — ПолучитьРодителя(), поиск по идентификатору — НайтиПоИдентификатору().

Удаление строки выглядит так:

СтрокаДерева = ДеревоЗначений.НайтиПоИдентификатору(Идентификатор);

Родитель = СтрокаДерева.ПолучитьРодителя();

Если Родитель = Неопределено Тогда

ДеревоЗначений.ПолучитьЭлементы().Удалить(СтрокаДерева);

Иначе

Родитель.ПолучитьЭлементы().Удалить(СтрокаДерева);

КонецЕсли;

Если удаление выполняется в серверной процедуре через РеквизитФормыВЗначение(), не забудьте после изменений вернуть данные обратно через ЗначениеВРеквизитФормы() — иначе изменения не отобразятся на форме. Однако для простого удаления строки дешевле работать с ДанныеФормыДерево напрямую, без преобразований: это быстрее и не сбрасывает состояние развёрнутых ветвей.

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

Типичные ошибки и как их избежать

Первая группа ошибок связана с неправильной коллекцией. Вызов Дерево.Строки.Удалить(Строка) для вложенной строки приведёт к ошибке «Неверное значение параметра» или молчаливому удалению не той строки, если передан индекс. Всегда определяйте родителя и удаляйте из его коллекции.

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

Третья группа — работа с формой. Попытка удалить строку через Дерево.Строки в управляемой форме, где реквизит имеет тип ДанныеФормыДерево, завершится ошибкой, потому что у этого объекта нет свойства Строки. Используйте ПолучитьЭлементы(). Также не забывайте проверять, что пользователь действительно выбрал строку: при пустом дереве ТекущаяСтрока равна Неопределено или -1.

Типичные ошибки
01
Вызов Удалить() у корневой коллекции для вложенной строки — ошибка или удаление не той строки; всегда удаляйте через родителя
02
Удаление строк внутри цикла Для Каждого по той же коллекции — пропуск строк или сбой итерации; удаляйте по индексу с конца или через промежуточный массив
03
Поиск только среди корневых строк (НайтиСтроки без параметра Истина) — нужная строка не находится, удаление не срабатывает
04
Использование свойства Строки у ДанныеФормыДерево в управляемой форме — ошибка, нужен метод ПолучитьЭлементы()
05
Отсутствие проверки выбранной строки на форме — ошибка при попытке удалить Неопределено

Проверка результата после удаления

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

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

Контрольные точки

Как проверить результат

Количество строк в коллекции родителя
Норма

Уменьшилось ровно на 1 после удаления одной строки

Тревожный признак

Не изменилось — удаление вызвано не у той коллекции или строка не найдена

Поиск удалённой строки через НайтиСтроки()
Норма

Массив результатов пуст

Тревожный признак

Строка находится — удалена другая строка с похожими данными

Отображение дерева на форме
Норма

Строка исчезла вместе с потомками, остальные ветви на месте

Тревожный признак

Исчезли лишние строки или форма не обновилась — проверьте преобразование реквизита

Частые вопросы

Можно ли восстановить удалённую строку дерева значений?

Нет, метод Удалить() необратим: строка и все её потомки удаляются без сохранения в корзину или журнал. Если восстановление может понадобиться, перед удалением скопируйте ветвь в отдельное дерево или храните резервную копию всего дерева, например, через ЗначениеВСтрокуВнутр().

Как удалить только потомков строки, оставив саму строку?

Вызовите Очистить() у коллекции строк этой строки: СтрокаГруппы.Строки.Очистить() для обычного дерева или СтрокаГруппы.ПолучитьЭлементы().Очистить() в управляемой форме. Сама строка и её данные в колонках сохранятся.

Почему НайтиСтроки() не находит строку, которая видна в дереве?

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

Как удалить строку, если известен только её индекс?

Метод Удалить() принимает индекс строки в коллекции: КоллекцияСтрок.Удалить(Индекс). Но индекс действителен только внутри конкретного уровня — индекс 0 у корня и индекс 0 у вложенной ветви указывают на разные строки. Надёжнее работать с объектом строки, а не с индексом.

Чем удаление строки дерева отличается от удаления строки таблицы значений?

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

Можно ли удалить строку дерева на клиенте без вызова сервера?

Да, если работать с реквизитом формы типа ДанныеФормыДерево напрямую: НайтиПоИдентификатору(), ПолучитьРодителя() и ПолучитьЭлементы().Удалить() доступны на клиенте в управляемой форме. Это избавляет от лишнего серверного вызова и ускоряет интерфейс.

Итог

Что важно запомнить

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