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

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

Работа с иерархическими структурами данных, такими как дерево значений, в управляемом приложении 1С часто вызывает вопросы у начинающих разработчиков. Типичная ситуация: на клиенте мы легко получаем данные через ТекущиеДанные, но при попытке перенести логику на сервер сталкиваемся с тем, что это свойство недоступно, а привычные методы поиска возвращают Неопределено. В этой статье мы подробно разберем, как устроена идентификация строк в дереве значений, почему «теряются» данные на сервере и какие подходы считаются наиболее надежными.

Различие между идентификатором и индексом

Для начала проанализируем, как платформа 1С:Предприятие 8.3 управляет строками в объекте ДанныеФормыДерево. Когда пользователь выбирает строку в визуальном элементе, свойство Элементы.МоеДерево.ТекущаяСтрока возвращает число. Важно понимать: это внутренний идентификатор строки, а не её порядковый номер (индекс).

Рассмотрим ключевые особенности этих идентификаторов:

  1. Уникальность: Идентификатор уникален в рамках текущего сеанса работы формы.
  2. Автоматическая генерация: Платформа сама назначает эти числа при добавлении строк.
  3. Неизменность при сортировке: Если вы отсортируете дерево, идентификатор строки останется прежним, в то время как индекс строки в коллекции изменится.
  4. Сброс при обновлении: Это самый важный момент. Если вы полностью очищаете дерево на сервере и заполняете его заново, старые идентификаторы «умирают». Даже если визуально дерево выглядит так же, с точки зрения системы — это новые объекты с новыми ID.

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

Разберем стандартный способ получения данных на сервере. Поскольку свойство ТекущиеДанные доступно только на клиенте, на сервере нам необходимо использовать метод коллекции данных формы. Рассмотрим пример кода в контекстном серверном вызове:


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

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

Почему поиск возвращает Неопределено?

Выясним причину, по которой код выше может не срабатывать. Часто разработчики сталкиваются с тем, что ИдентификаторСтроки равен, например, 8, но НайтиПоИдентификатору(8) возвращает Неопределено. Это происходит в следующих случаях:

  1. Перезаполнение данных: Если в процессе серверного вызова (или непосредственно перед ним) дерево было очищено и заполнено заново через МоеДерево.ПолучитьЭлементы().Очистить(), то идентификатор 8 больше не принадлежит ни одной строке.
  2. Внеконтекстный вызов: Если вы используете процедуру &НаСервереБезКонтекста, вы не имеете доступа к объекту Элементы. В этом случае идентификатор нужно передавать с клиента в качестве параметра.
  3. Конфликт событий: Как в примере из обсуждения на форуме, если в событии ПриАктивизацииСтроки одного дерева происходит очистка другого дерева, то попытка найти активную строку во втором дереве будет обречена на провал, так как её физически больше нет.

Оптимизация: Передача идентификатора с клиента

Хорошей практикой считается получение идентификатора на клиенте и передача его на сервер. Это делает ваш код более «прозрачным» и позволяет использовать внеконтекстные серверные вызовы, которые работают быстрее, так как не передают всё состояние формы (весь контекст) на сервер и обратно.


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

&НаСервереБезКонтекста
Процедура ВыполнитьОбработкуНаСервере(Знач Идентификатор)
    // В данном случае дерево должно быть передано как адрес во временном хранилище
    // или обработано через контекст, если это необходимо.
КонецПроцедуры

Альтернативный подход: Собственные ключи строк

Если ваша задача подразумевает частое обновление структуры дерева (например, динамическое считывание остатков или статусов), полагаться на системные идентификаторы опасно. В таких случаях мы рекомендуем использовать «велосипед», ставший стандартом индустрии — пользовательские ключи связи.

Суть метода заключается в добавлении в дерево скрытых колонок, например, КлючСтроки и КлючРодителя. Рассмотрим, как реализовать поиск по такому ключу:


// Функция для рекурсивного поиска по пользовательскому реквизиту
&НаСервере
Функция НайтиСтрокуПоКлючу(КоллекцияЭлементов, ИмяРеквизита, ЗначениеКлюча)
    
    Для Каждого СтрокаДерева Из КоллекцияЭлементов Цикл
        Если СтрокаДерева[ИмяРеквизита] = ЗначениеКлюча Тогда
            Возврат СтрокаДерева;
        КонецЕсли;
        
        // Рекурсивный поиск в подчиненных элементах
        ПодчиненныйПоиск = НайтиСтрокуПоКлючу(СтрокаДерева.ПолучитьЭлементы(), ИмяРеквизита, ЗначениеКлюча);
        Если ПодчиненныйПоиск <> Неопределено Тогда
            Возврат ПодчиненныйПоиск;
        КонецЕсли;
    КонецЦикла;
    
    Возврат Неопределено;
    
КонецФункции

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

Важное замечание о производительности

Проанализируем ситуацию с событием ПриАктивизацииСтроки. Если вы вызываете серверную процедуру непосредственно в этом обработчике, помните, что он срабатывает при каждом перемещении курсора пользователем. Это может привести к значительным задержкам интерфейса. Чтобы избежать «фризов», рекомендуется использовать ПодключитьОбработчикОжидания. Это позволит выполнять серверный вызов только после того, как пользователь прекратит навигацию по дереву и задержится на одной строке хотя бы на 0.3–0.5 секунды.

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

← На главную