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