Перебор строк и работа с таблицей формы в 1С 8.3: код, ограничения и альтернативы
Содержание
Обойти табличное поле в 1С — значит перебрать не сам элемент формы, а данные, к которым он привязан. В управляемой форме это Для Каждого Строка Из Объект.Товары Цикл, где Объект.Товары — путь к данным таблицы формы. Выделенные строки берут из Элементы.Товары.ВыделенныеСтроки, текущую — из Элементы.Товары.ТекущиеДанные.
Способ зависит от того, что лежит под таблицей. Табличная часть и таблица значений перебираются целиком циклом. Дерево значений требует рекурсии. Динамический список строк на клиенте не хранит вообще — его обходят запросом на сервере.
Перебор строк таблицы формы в коде
Элемент «Таблица формы» (в обычных формах — «Табличное поле») только показывает данные. Цикл всегда идёт по реквизиту, указанному в свойстве ПутьКДанным.
Все строки
&НаКлиенте
Процедура ПересчитатьСуммы(Команда)
Для Каждого Строка Из Объект.Товары Цикл
Строка.Сумма = Строка.Количество * Строка.Цена;
КонецЦикла;
КонецПроцедуры
Тот же код работает и на сервере. Если строк больше нескольких сотен и расчёт тяжёлый, переносите его в серверную процедуру: один вызов сервера дешевле, чем долгий цикл в тонком клиенте.
Только выделенные строки
ВыделенныеСтроки у таблицы формы с табличной частью или таблицей значений — это массив идентификаторов, а не самих строк. Строку получают через НайтиПоИдентификатору:
&НаКлиенте
Процедура ОбнулитьСкидку(Команда)
Для Каждого Ид Из Элементы.Товары.ВыделенныеСтроки Цикл
Строка = Объект.Товары.НайтиПоИдентификатору(Ид);
Если Строка <> Неопределено Тогда
Строка.Скидка = 0;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
У динамического списка в ВыделенныеСтроки лежат ключи записей — для справочников и документов это ссылки. Их передают на сервер и обрабатывают там.
Удаление строк в цикле
Удалять строки внутри Для Каждого нельзя: после удаления сдвигаются индексы и цикл пропускает соседнюю строку. Правильно — идти с конца, зафиксировав количество до начала цикла:
Всего = Объект.Товары.Количество();
Для Сч = 1 По Всего Цикл
Индекс = Всего - Сч;
Строка = Объект.Товары[Индекс];
Если Строка.Количество = 0 Тогда
Объект.Товары.Удалить(Индекс);
КонецЕсли;
КонецЦикла;
При пяти строках цикл пройдёт индексы 4, 3, 2, 1, 0. Удаление строки с большим индексом не меняет номера строк перед ней, поэтому ничего не теряется.
Для выделенных строк порядок другой: сначала соберите строки в массив по идентификаторам, затем удаляйте по массиву. Пока вы бежите по ВыделенныеСтроки и удаляете, массив выделения меняется.
Обычные формы
В толстом клиенте с обычными формами у элемента ЭлементыФормы.Товары свойство ВыделенныеСтроки возвращает коллекцию самих строк, а ТекущаяСтрока — строку источника. Перебор данных — тот же цикл по табличной части или таблице значений, которая указана в свойстве Данные элемента.
Если нужно перебрать строки ровно в том порядке и с тем отбором, которые видит пользователь, помните: сортировка и отбор в таблице формы касаются отображения. Цикл по Объект.Товары идёт в порядке хранения и по всем строкам.
Где стандартная таблица упирается в потолок
Таблица формы удобна, пока данные плоские и их немного. Узкие места видны сразу:
- 📦 Объём. Таблица значений в реквизите формы целиком передаётся между сервером и клиентом. 50 000 строк по 15 колонок — это мегабайты трафика при каждом серверном вызове с контекстом.
- 🌳 Иерархия. Плоская таблица не умеет сворачивать группы. Для вложенности нужен другой тип данных.
- 🧩 Динамические колонки. Набор колонок задаётся в конфигураторе. Если он известен только в момент работы (например, по числу складов), колонки придётся создавать кодом.
- 🎨 Оформление. Отдельную ячейку нельзя «покрасить» напрямую, только через условное оформление по правилу.
- 🧾 Сложная вёрстка. Объединённые ячейки, шапки в несколько этажей, графика внутри — это уже не таблица формы.
У каждого ограничения есть свой обходной путь, и почти всегда без переписывания формы с нуля.
Дерево значений вместо плоского списка
Когда строки вложены друг в друга (группа → подгруппа → номенклатура), реквизит формы делают типа ДеревоЗначений. На форме он отображается таблицей с узлами, которые разворачиваются.
Заполнение на сервере
&НаСервере
Процедура ЗаполнитьДерево()
Дерево = РеквизитФормыВЗначение("ДеревоТоваров");
Дерево.Строки.Очистить();
Группа = Дерево.Строки.Добавить();
Группа.Наименование = "Канцтовары";
Товар = Группа.Строки.Добавить();
Товар.Наименование = "Бумага А4";
Товар.Количество = 10;
ЗначениеВРеквизитФормы(Дерево, "ДеревоТоваров");
КонецПроцедуры
Рекурсивный обход
На клиенте у дерева нет свойства Строки — подчинённые элементы получают методом ПолучитьЭлементы():
&НаКлиенте
Функция СуммаПоУзлу(Элементы)
Итог = 0;
Для Каждого Узел Из Элементы Цикл
Подчиненные = Узел.ПолучитьЭлементы();
Если Подчиненные.Количество() = 0 Тогда
Итог = Итог + Узел.Количество;
Иначе
Итог = Итог + СуммаПоУзлу(Подчиненные);
КонецЕсли;
КонецЦикла;
Возврат Итог;
КонецФункции
// Вызов: Всего = СуммаПоУзлу(ДеревоТоваров.ПолучитьЭлементы());
На сервере после РеквизитФормыВЗначение обход тот же, только через Строка.Строки. Для поиска по всем уровням у коллекции строк дерева есть Найти и НайтиСтроки с параметром включения подчинённых.
Развернуть узел программно можно методом таблицы формы Развернуть(ИдентификаторСтроки, Истина) — второй параметр раскрывает все вложенные уровни. На дереве в тысячи узлов полное раскрытие заметно тормозит, лучше раскрывать первый уровень.
Колонки и их видимость из кода
Скрыть или показать колонку
Простейший случай — колонка уже есть в форме:
Элементы.ТоварыСкидка.Видимость = ПолучитьФункциональнуюОпцию("ИспользоватьСкидки");
Элементы.ТоварыЦена.ТолькоПросмотр = Не РольДоступна("ПолныеПрава");
Имя элемента колонки смотрите в дереве элементов формы, оно обычно составлено из имени таблицы и имени поля.
Добавить колонку, которой не было в конфигураторе
Для управляемой формы нужно два шага: создать реквизит и создать поле формы, которое на него смотрит.
&НаСервере
Процедура ДобавитьКолонкуСклада(ИмяКолонки, Заголовок)
Добавляемые = Новый Массив;
Добавляемые.Добавить(Новый РеквизитФормы(ИмяКолонки,
Новый ОписаниеТипов("Число"), "ОстаткиПоСкладам", Заголовок));
ИзменитьРеквизиты(Добавляемые);
Поле = Элементы.Добавить("ОстаткиПоСкладам" + ИмяКолонки,
Тип("ПолеФормы"), Элементы.ОстаткиПоСкладам);
Поле.Вид = ВидПоляФормы.ПолеВвода;
Поле.ПутьКДанным = "ОстаткиПоСкладам." + ИмяКолонки;
КонецПроцедуры
Здесь ОстаткиПоСкладам — реквизит типа таблица значений, третий параметр конструктора РеквизитФормы указывает, внутри какого реквизита создать колонку.
Добавленные кодом реквизиты не сохраняются в метаданных. При повторном открытии формы колонки создаются заново, обычно в ПриСозданииНаСервере. Перед повторным добавлением проверяйте, нет ли элемента с таким именем, иначе получите ошибку о неуникальном имени.
В обычной форме колонку добавляют проще: ЭлементыФормы.Таблица.Колонки.Добавить(Имя, Заголовок) и задают ей Данные, если таблица связана с таблицей значений.
Постраничный вывод больших выборок
Динамический список уже работает порциями: платформа сама подгружает строки по мере прокрутки. Поэтому первое решение для больших справочников и журналов — динамический список, а не таблица значений.
Если вывод всё же собирается вручную (результат сложного расчёта, данные из внешней системы), делайте листание по ключу. В языке запросов 1С нет конструкции смещения, есть только ПЕРВЫЕ N. Приём такой:
- Отсортируйте выборку по уникальному полю — например, по коду или по дате с номером.
- Возьмите первые 100 строк, запомните значение ключа последней строки.
- Для следующей страницы добавьте условие «ключ больше запомненного» и снова
ПЕРВЫЕ 100. - Для кнопки «Назад» храните стек ключей начала каждой страницы.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 100
| Номенклатура.Ссылка КАК Ссылка,
| Номенклатура.Код КАК Код,
| Номенклатура.Наименование КАК Наименование
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Код > &ПоследнийКод
|УПОРЯДОЧИТЬ ПО
| Код";
Запрос.УстановитьПараметр("ПоследнийКод", ПоследнийКод);
Число в ПЕРВЫЕ параметром не задаётся. Если размер страницы настраивается, подставляйте его в текст запроса через СтрЗаменить до выполнения.
Сравнение «больше» по строковому коду работает корректно, только если коды одной длины. Для числовых кодов или смешанных данных листайте по ссылке с сортировкой по ней же или по дате создания.
Оформление ячеек по условию
Покрасить отдельную ячейку в управляемой форме можно только правилом: «если условие — применить оформление к полю». Правила задают в свойствах формы или кодом через УсловноеОформление.
&НаСервере
Процедура ВыделитьОтрицательныеОстатки()
Элемент = УсловноеОформление.Элементы.Добавить();
Отбор = Элемент.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
Отбор.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Объект.Товары.Остаток");
Отбор.ВидСравнения = ВидСравненияКомпоновкиДанных.Меньше;
Отбор.ПравоеЗначение = 0;
Элемент.Оформление.УстановитьЗначениеПараметра("ЦветТекста", WebЦвета.Красный);
Поле = Элемент.Поля.Элементы.Добавить();
Поле.Поле = Новый ПолеКомпоновкиДанных("ТоварыОстаток");
КонецПроцедуры
Пути здесь разные: в отборе указывается путь к данным (Объект.Товары.Остаток), а в поле оформления — имя элемента формы (ТоварыОстаток). Перепутанный путь — самая частая причина, по которой правило «не работает
».
Что ещё можно менять через оформление:
| Параметр | Что делает |
|---|---|
ЦветТекста, ЦветФона | цвет шрифта и заливки ячейки |
Шрифт | жирный, курсив, размер |
Текст | заменяет отображаемое значение, например пустое поле на «не указано» |
Видимость | скрывает значение в ячейке |
ТолькоПросмотр | запрещает редактирование ячейки по условию |
В обычных формах для того же есть событие табличного поля ПриВыводеСтроки(Элемент, ОформлениеСтроки, ДанныеСтроки). В нём доступна каждая ячейка: ОформлениеСтроки.Ячейки.Остаток.ЦветФона. В управляемых формах такого события нет, поэтому логику «покрасить по сложной формуле» переносят в отдельную колонку-признак и делают правило по ней.
Поле HTML-документа и табличный документ для сложного вывода
Когда таблица формы уже не справляется с вёрсткой, есть два пути. Какой выбрать — по задаче:
| Элемент | Когда брать | Ограничения |
|---|---|---|
| Поле табличного документа | отчёты, печатные формы, сводные таблицы, объединённые шапки, расшифровка по клику | редактирование данных неудобное, это вывод, а не ввод |
| Поле HTML-документа | карточки, графики на JavaScript, нестандартная вёрстка, встроенные справки | данные надо собирать в HTML строкой, поведение зависит от встроенного браузера клиента |
| Таблица формы | ввод и правка строк, списки с отбором | плоская или иерархическая структура, фиксированная сетка |
Для отчётов почти всегда лучше поле табличного документа: его заполняют макетом или схемой компоновки данных, а клик по ячейке ловят обработкой расшифровки. Пользователь может сохранить результат в Excel или PDF стандартными средствами.
Поле HTML-документа подключают к реквизиту типа строка. В реквизит записывают готовую разметку, а реакцию на клик обрабатывают в событии ПриНажатии(Элемент, ДанныеСобытия, СтандартнаяОбработка): по ДанныеСобытия можно понять, на какой элемент страницы нажали. Если в разметку подставляются данные из базы, экранируйте символы <, > и & — иначе одно наименование с угловой скобкой ломает всю страницу.
Как не тормозить на больших массивах данных
Скорость таблицы формы чаще всего убивает не сам объём, а количество обращений к серверу. Проверенные приёмы:
- Один серверный вызов вместо цикла с вызовами. Обработка
Для Каждогона клиенте, внутри которой вызывается серверная функция, делает столько вызовов, сколько строк. Соберите нужное в массив и передайте за раз. &НаСервереБезКонтекстатам, где форма не нужна. Директива&НаСерверетаскает на сервер весь контекст формы, включая таблицу. Если функции нужны только параметры, контекст ей не нужен.- Временное хранилище для тяжёлых таблиц. Таблицу значений, которую не надо показывать целиком, кладите через
ПоместитьВоВременноеХранилищеи храните на форме только адрес. - Минимум колонок в запросе. Каждая лишняя колонка в реквизите формы — это трафик при каждом вызове с контекстом.
- Динамический список для всего, что больше нескольких тысяч строк. Он читает только видимую порцию и не держит выборку в памяти формы.
- Без
ТекущиеДанныев циклах. Это свойство про строку под курсором, перебирать через него таблицу бессмысленно и медленно.
Не переносите в форму таблицу на десятки тысяч строк ради «чтобы было под рукой» — у пользователей на удалённом рабочем столе или в веб-клиенте форма будет открываться десятки секунд.
Чтобы обработать все строки динамического списка с учётом отбора пользователя, не пытайтесь «прокрутить» список на клиенте. Передайте на сервер значения отбора и выполните запрос с теми же условиями — результат совпадёт с тем, что видно на экране, и не будет ограничен загруженной порцией.
Частые вопросы
Чем табличное поле отличается от таблицы формы?
Как узнать номер текущей строки?
Элементы.Товары.ТекущиеДанные и прочитайте поле НомерСтроки, если таблица связана с табличной частью. У таблицы значений номера нет, используйте индекс: Объект.Товары.Индекс(Строка) на сервере.Почему цикл по таблице не видит отбор пользователя?
Можно ли перебрать все строки динамического списка на клиенте?
Почему после ИзменитьРеквизиты колонка пустая?
ПутьКДанным поля и заполните таблицу уже после добавления реквизита.