Как программно получить текущую строку дерева значений на сервере в управляемом интерфейсе?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При работе с управляемыми формами в 1С программисты часто сталкиваются с ситуацией, когда на клиенте идентификатор текущей строки дерева значений (свойство ТекущаяСтрока) представлен числом, но при попытке использовать это число на сервере для получения строки из объекта ДеревоЗначений возникает ошибка. Часто данные в такие структуры попадают через специализированные инструменты чтения файлов в дерево значений — для этой задачи отлично подойдёт обработка загрузки иерархических данных из файлов. Ошибка происходит из-за путаницы между внутренним идентификатором строки формы и индексом строки в коллекции данных. В этой статье мы подробно разберем, как правильно связать клиентские данные с серверными и какие методы платформы 1С для этого предназначены.

Разберем причину возникновения проблемы

Рассмотрим стандартную ситуацию: на форме расположено дерево значений, связанное с реквизитом ДеревоОбъектов — спроектировать такой интерфейс без доработки конфигурации поможет конструктор рабочих столов и динамических форм. Подобная иерархическая модель часто применяется для автоматического анализа метаданных и графа зависимостей. Проанализируем, что происходит, когда мы пытаемся получить строку:

  1. На клиенте через Элементы.МоеДерево.ТекущаяСтрока мы получаем идентификатор (например, 694).
  2. На сервере мы преобразуем реквизит формы в объект: Дер = ДанныеФормыВЗначение(ДеревоОбъектов, Тип("ДеревоЗначений")).
  3. Пытаемся обратиться к строке: Дер.Строки.Получить(694) — и получаем ошибку «Индекс находится за границами коллекции».

Выясним причину: свойство ТекущаяСтрока возвращает уникальный идентификатор строки в контексте данных формы. Этот идентификатор не является порядковым индексом строки в массиве. Более того, дерево значений имеет иерархическую структуру, и индекс 0 может быть у первой строки каждого уровня вложенности, в то время как идентификатор всегда уникален для всего дерева в рамках текущего сеанса формы.

Решение 1: Использование метода НайтиПоИдентификатору

Самый простой и эффективный способ получить данные текущей строки на сервере — использовать встроенный метод реквизита формы. Нам не нужно преобразовывать все дерево в объект ДеревоЗначений, если мы хотим просто прочитать или изменить данные текущей строки. Рассмотрим этот метод по шагам.

Шаг 1. Передаем идентификатор с клиента на сервер.


&НаКлиенте
Процедура ОбработатьТекущуюСтроку()
    // Получаем идентификатор (число)
    ИдентификаторСтроки = ЭтаФорма.Элементы.ДеревоОбъектов.ТекущаяСтрока;
    
    Если ИдентификаторСтроки <> Неопределено Тогда
        ОбработатьНаСервере(ИдентификаторСтроки);
    КонецЕсли;
КонецПроцедуры

Шаг 2. Находим строку на сервере через ДанныеФормы.

Проанализируем серверный код. Вместо преобразования всего дерева, обратимся напрямую к реквизиту формы ДеревоОбъектов (тип ДанныеФормыДерево):


&НаСервере
Процедура ОбработатьНаСервере(ИдентификаторСтроки)
    // Ищем элемент данных формы по полученному ID
    СтрокаДерева = ДеревоОбъектов.НайтиПоИдентификатору(ИдентификаторСтроки);
    
    Если СтрокаДерева <> Неопределено Тогда
        // Теперь мы можем работать с колонками строки
        ЗначениеКолонки = СтрокаДерева.МояКолонка;
        СтрокаДерева.МояКолонка = "Новое значение";
    КонецЕсли;
КонецПроцедуры

Обратите внимание: переменная СтрокаДерева в данном случае имеет тип ДанныеФормыЭлементДерева. Это позволяет работать с ней как с обычной строкой, и все изменения сразу отразятся на форме.

Решение 2: Работа с объектом ДеревоЗначений и преобразование типов

Иногда нам все же необходимо получить именно объектную строку (тип СтрокаДереваЗначений), например, для передачи в функцию общего модуля, которая не умеет работать с данными формы. Это часто требуется, если вы используете универсальную функцию получения результата СКД для расчета сложных иерархических таблиц. Посмотрим, как сделать это правильно, избежав ошибки «Несоответствие типов» — для написания и тестирования подобных алгоритмов пригодится комплексная консоль инструментов разработчика.

Как мы уже выяснили, ДанныеФормыВЗначение нельзя применить к отдельной строке. Рассмотрим правильный алгоритм совместной работы:


&НаСервере
Функция ПолучитьОбъектнуюСтроку(ИдСтроки)
    // 1. Сначала находим строку в данных формы
    ФСтрока = ДеревоОбъектов.НайтиПоИдентификатору(ИдСтроки);
    
    // 2. Преобразуем все дерево в объект
    ОбъектДерево = ДанныеФормыВЗначение(ДеревоОбъектов, Тип("ДеревоЗначений"));
    
    // 3. Чтобы найти эту же строку в объекте ДеревоЗначений, 
    // нам понадобится какой-то уникальный ключ, так как ID формы там нет.
    // Если ключа нет, придется использовать рекурсивный поиск по значениям колонок.
    // Рассмотрим пример поиска по уникальному ключу "ИдентификаторСтроки"
    ПараметрыПоиска = Новый Структура("КлючСвязи", ФСтрока.КлючСвязи);
    НайденныеСтроки = ОбъектДерево.Строки.НайтиСтроки(ПараметрыПоиска, Истина);
    
    Если НайденныеСтроки.Количество() > 0 Тогда
        Возврат НайденныеСтроки[0]; // Возвращает тип СтрокаДереваЗначений
    КонецЕсли;
    
    Возврат Неопределено;
КонецФункции

Решение 3: Использование скрытого ключа (Best Practice)

Разберем ситуацию, когда структура дерева часто меняется, обновляется или сортируется. В этом случае полагаться на НайтиПоИдентификатору может быть рискованно, если дерево полностью перезаполняется на сервере (идентификаторы строк при этом могут перегенерироваться). Изучая современные эффективные приемы построения интерфейсов, можно прийти к выводу, что использование собственных ключей — наиболее надежный метод.

Проанализируем подход с созданием собственного ключа. Это наиболее надежный метод, который рекомендуют опытные разработчики:

  1. Добавим в дерево значений колонку УникальныйКлюч (тип Строка или УникальныйИдентификатор).
  2. При заполнении дерева на сервере будем записывать туда уникальное значение: НоваяСтрока.УникальныйКлюч = Строка(Новый УникальныйИдентификатор());.
  3. На клиенте мы получаем текущие данные строки: Ключ = Элементы.ДеревоОбъектов.ТекущиеДанные.УникальныйКлюч;.
  4. На сервере ищем строку методом НайтиСтроки по всей коллекции, независимо от её иерархии.

Посмотрим на пример реализации поиска в объекте ДеревоЗначений с произвольной иерархией:


&НаСервере
Функция НайтиСтрокуПоКлючу(ДеревоОбъект, ЗначениеКлюча)
    ПараметрыПоиска = Новый Структура("УникальныйКлюч", ЗначениеКлюча);
    Найденные = ДеревоОбъект.Строки.НайтиСтроки(ПараметрыПоиска, Истина);
    
    Если Найденные.Количество() > 0 Тогда
        Возврат Найденные[0];
    КонецЕсли;
    
    Возврат Неопределено;
КонецФункции

Типичные ошибки при работе с иерархией

Давайте проанализируем, почему метод Индекс() в первом сообщении темы возвращал значения от 0 до 10, хотя идентификатор был 694. Метод Индекс() возвращает позицию строки внутри её родительской коллекции (т.е. среди «собратьев»). При разборе сложных структур бывает полезен просмотрщик JSON-структуры, чтобы наглядно увидеть вложенность данных.

Если строка находится на втором уровне вложенности и является там первой, её индекс будет 0. Если на первом уровне она также первая — её индекс тоже будет 0. Именно поэтому поиск по индексу в дереве значений невозможен без указания конкретного уровня итерации.

Важные моменты для запоминания:

Используя эти методы, мы обеспечим стабильную работу приложения и корректную передачу данных между клиентской и серверной частью управляемой формы 1С.

← На главную