Как найти строку в ДеревеЗначений на форме и получить объект ДанныеФормыЭлементДерева

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

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

В этой статье мы разберем, как правильно организовать поиск в иерархических структурах формы (тип ДанныеФормыДерево) и получить объект ДанныеФормыЭлементДерева, с которым можно работать на клиенте и сервере.

Проблема разрыва связи между объектом и реквизитом

Рассмотрим стандартную ошибку. Программист пытается выполнить поиск на сервере следующим образом:


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

Этот код вернет объект, который "умрет" сразу после завершения серверного вызова, так как он не является частью реквизита формы. Проанализируем правильные подходы к решению этой задачи.

Решение 1: Рекурсивный поиск по элементам ДанныеФормыДерево

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


&НаКлиенте
Функция НайтиСтрокуВЭлементах(Элементы, СтруктураПоиска) Экспорт

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

КонецФункции

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

Решение 2: Использование внутренних идентификаторов (самый надежный метод)

Проанализируем более профессиональный подход. У каждой строки в ДанныеФормыДерево есть уникальный идентификатор, который сохраняется в течение жизни формы. Если нам всё же необходимо использовать серверные методы поиска ДереваЗначений (например, из-за их высокой скорости), мы можем связать объекты через этот идентификатор.

Шаг 1: Подготовка. При заполнении дерева на сервере создадим в нем скрытую колонку ИдентификаторСтроки (тип Число).


// При заполнении дерева на сервере
НоваяСтрока = ДеревоПозиций.ПолучитьЭлементы().Добавить();
НоваяСтрока.Наименование = "Пример";
// Сохраняем внутренний ID строки формы в нашу колонку
НоваяСтрока.ИдентификаторСтроки = НоваяСтрока.ПолучитьИдентификатор();

Шаг 2: Поиск. Теперь на сервере мы можем использовать мощный поиск по ДеревуЗначений, найти строку, получить из нее сохраненный ИдентификаторСтроки и мгновенно получить элемент формы.


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

Решение 3: Поиск всех вхождений в массив

Если нам нужно найти не одну строку, а все подходящие под условие, мы модифицируем рекурсивную функцию так, чтобы она собирала результаты в массив. Это часто требуется для массовой обработки узлов дерева (например, проставление флажков).


&НаКлиенте
Функция НайтиВсеСтрокиВЭлементах(Элементы, СтруктураПоиска, Результат = Неопределено) Экспорт
    
    Если Результат = Неопределено Тогда
        Результат = Новый Массив;
    КонецЕсли;

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

Важные рекомендации по оптимизации

Проанализируем несколько важных моментов, которые помогут избежать ошибок при работе с деревьями на формах:

  1. Минимизируйте серверные вызовы. Если поиск выполняется часто (например, при вводе каждого символа в поле поиска), лучше реализовать рекурсию на клиенте, как показано в Решении 1. Это снимет нагрузку с сервера и сделает интерфейс более отзывчивым.
  2. Используйте НайтиПоИдентификатору. Это самый быстрый способ получения строки. Даже если вы не храните ID в колонке, помните, что метод НайтиПоИдентификатору() работает очень эффективно, если вы передаете ID между клиентскими процедурами.
  3. Помните о типах. Тип ДанныеФормыЭлементДерева доступен и на клиенте, и на сервере, в то время как СтрокаДереваЗначений — только на сервере в контексте объекта. Никогда не пытайтесь передать СтрокуДереваЗначений на клиент — это вызовет ошибку сериализации.
  4. Синхронизация. Если вы изменили данные в ДеревеЗначений на сервере (через РеквизитФормыВЗначение), не забудьте вызвать ЗначениеВРеквизитФормы, иначе ваши изменения не отобразятся в таблице на форме.

Выясним причину, почему метод с индексами (путь вида "0,2,1") менее предпочтителен: при программном удалении или добавлении строк индексы смещаются. Поэтому использование метода НайтиПоИдентификатору() или прямой рекурсивный поиск по значениям ключевых полей являются наиболее стабильными вариантами автоматизации бизнеса на платформе 1С.

← На главную