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