При разработке на платформе «1С:Предприятие 8.3» программисты часто сталкиваются с задачей программного перебора колонок дерева значений. На первый взгляд задача кажется тривиальной, однако в управляемом приложении возникает серьезная преграда: различие между типами данных на стороне клиента и сервера. Разберем детально, почему обычный обход в цикле может «не прокатывать», и какими способами можно гарантированно получить имена всех колонок.
Прежде чем переходить к коду, проанализируем ситуацию. В 1С существует три принципиально разных сущности, которые мы привыкли называть «деревом»:
Колонки, к которой можно обратиться напрямую.Колонки. Именно поэтому попытка написать Для каждого Кол Из Дерево.Колонки на клиенте вызывает ошибку.ПодчиненныеЭлементы, но они отражают только то, что выведено на экран (визуальные поля), а не реальный состав данных.Если нам нужно получить состав колонок в серверном контексте, самый простой и надежный способ — преобразовать ДанныеФормыДерево в полноценное ДеревоЗначений. Рассмотрим пример кода, который часто используется в таких случаях:
&НаСервере
Процедура ПолучитьИменаКолонокНаСервере()
// Преобразуем реквизит формы в объект ДеревоЗначений
ДеревоОбъект = РеквизитФормыВЗначение("ИмяВашегоРеквизитаДерева");
// Теперь нам доступна коллекция Колонки
Для Каждого Колонка Из ДеревоОбъект.Колонки Цикл
ИмяКолонки = Колонка.Имя;
Сообщить("Найдена колонка: " + ИмяКолонки);
КонецЦикла;
КонецПроцедуры
Важный нюанс: Метод РеквизитФормыВЗначение является довольно «тяжелым», так как он полностью копирует все данные из формы в новый объект в памяти. Если ваше дерево содержит тысячи строк, это может привести к заметным задержкам. В таких случаях лучше использовать метод Выгрузить(), который работает быстрее:
&НаСервере
Процедура ПолучитьИменаКолонокЧерезВыгрузку()
// Выгружаем данные реквизита в чистое дерево значений
ДеревоЗначений = Объект.ИмяДерева.Выгрузить();
Для Каждого Колонка Из ДеревоЗначений.Колонки Цикл
// Работаем с именами колонок
ТекИмя = Колонка.Имя;
КонецЦикла;
КонецПроцедуры
Если нам нужно узнать структуру колонок, не затрагивая сами данные (например, когда дерево очень большое), проанализируем более изящный способ через метаданные формы. Метод ПолучитьРеквизиты() возвращает массив описаний всех реквизитов формы.
&НаСервере
Функция ПолучитьСписокКолонокБезДанных(ИмяРеквизитаДерева)
МассивИмен = Новый Массив;
// Получаем структуру всех реквизитов формы
ВсеРеквизиты = ПолучитьРеквизиты(ИмяРеквизитаДерева);
Для Каждого ОписаниеРеквизита Из ВсеРеквизиты Цикл
// Нас интересуют подчиненные элементы (колонки) указанного реквизита
МассивИмен.Добавить(ОписаниеРеквизита.Имя);
КонецЦикла;
Возврат МассивИмен;
КонецФункции
Иногда возникает необходимость получить имена колонок прямо на клиенте без серверного вызова. Как мы выяснили, у данных формы нет коллекции колонок. Единственный путь здесь — обращение к коллекции Элементы. Посмотрим на пример:
&НаКлиенте
Процедура УзнатьКолонкиНаКлиенте()
Для Каждого ЭлементКолонка Из Элементы.ДеревоЦен.ПодчиненныеЭлементы Цикл
// Имя элемента формы обычно совпадает с именем колонки в данных
ИмяПутиКДанным = ЭлементКолонка.ПутьКДанным;
Сообщить("Элемент формы связан с данными: " + ИмяПутиКДанным);
КонецЦикла;
КонецПроцедуры
Внимание: У этого способа есть критическое ограничение. Вы увидите только те колонки, для которых созданы визуальные элементы на форме. Если колонка существует в данных, но она скрыта или для нее не создано «Поле ввода», этот цикл ее проигнорирует. На клиенте достоверного способа получить все колонки данных без серверного вызова не существует.
Чтобы избежать постоянных обращений к серверу и тяжелых операций конвертации, профессиональные разработчики используют методику кэширования. Суть проста: при создании формы или при динамическом изменении состава колонок мы сохраняем их имена в служебный реквизит.
Выполним следующие шаги:
СписокКолонокКэш с типом Строка или ФиксированныйМассив.ПриСозданииНаСервере заполним этот реквизит.
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Предположим, колонки создаются динамически
// Собираем их имена в строку через запятую
СтрокаИмен = "";
Для Каждого Колонка Из РеквизитФормыВЗначение("Дерево").Колонки Цикл
СтрокаИмен = СтрокаИмен + Колонка.Имя + ",";
КонецЦикла;
// Сохраняем в реквизит формы, доступный на клиенте
ЭтаФорма.СписокКолонокКэш = СтрокаИмен;
КонецПроцедуры
Теперь на клиенте нам достаточно просто прочитать значение СписокКолонокКэш и разбить его в массив функцией СтрРазделить(), что работает мгновенно.
Иногда при обходе колонок вы можете встретить странные имена вроде ВидЦены2dfe5138c380.... Выясним причину этого явления. Это происходит в двух случаях:
ИзменитьРеквизиты() разработчики часто добавляют уникальный идентификатор (GUID), чтобы гарантированно избежать конфликта имен в сложных динамических формах.Чтобы корректно работать с такими именами, рекомендуется всегда использовать свойство ПутьКДанным у элементов формы или хранить соответствие «Понятное имя — Системное имя» в отдельном соответствии (Map).
Подводя итог, можно сказать: если вам нужно гарантированно получить все колонки — делайте это на сервере через Выгрузить() или ПолучитьРеквизиты(). Если важна производительность на клиенте — используйте кэширование состава колонок в строковом реквизите формы.