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