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