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