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