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

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

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

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


МассивДобавляемыхРеквизитов = Новый Массив;
МассивДобавляемыхРеквизитов.Добавить(Новый РеквизитФормы("Дерево", Новый ОписаниеТипов("ДеревоЗначений")));
Для Каждого Колонка Из Дерево.Колонки Цикл
    МассивДобавляемыхРеквизитов.Добавить(Новый РеквизитФормы(Колонка.Имя, Колонка.ТипЗначения, "Дерево"));
КонецЦикла;
ИзменитьРеквизиты(МассивДобавляемыхРеквизитов);

// Преобразование объекта ДеревоЗначений в реквизит формы
ЗначениеВРеквизитФормы(Дерево, "Дерево");

// Создание элемента формы
ЭлементДерево = Элементы.Добавить("Дерево", Тип("ТаблицаФормы"));
ЭлементДерево.ПутьКДанным = "Дерево";
ЭлементДерево.Отображение = ОтображениеТаблицы.Дерево;

Для Каждого Колонка Из Дерево.Колонки Цикл
    НовыйЭлемент = Элементы.Добавить(Колонка.Имя, Тип("ПолеФормы"), ЭлементДерево);
    НовыйЭлемент.Вид = ВидПоляФормы.ПолеВвода;
    НовыйЭлемент.ПутьКДанным = "Дерево." + Колонка.Имя;
КонецЦикла;

Выясним причину возникновения ошибки

При повторном выполнении этого фрагмента кода платформа пытается повторно добавить в структуру формы уже существующие реквизиты и элементы интерфейса:

  1. Метод ИзменитьРеквизиты() не может создать реквизит с именем Дерево, так как реквизит с таким именем уже зарегистрирован в структуре формы.
  2. Метод Элементы.Добавить() вызывает исключение, если элемент с переданным именем уже существует в коллекции формы.

Посмотрим на основные подходы к решению этой проблемы: от оптимизации работы с неизменяемой структурой до корректного удаления и пересоздания динамических реквизитов.

Решение 1. Однократное создание структуры и обновление только данных (рекомендуемый подход)

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

Вариант А. Создание реквизита в конфигураторе:

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


&НаСервере
Процедура СформироватьДеревоНаСервере()
    // Создаем и заполняем прикладной объект ДеревоЗначений
    ДеревоОбъект = Новый ДеревоЗначений;
    // ... наполнение колонок и строк ...
    
    // Переносим данные в существующий реквизит управляемой формы
    ЗначениеВРеквизитФормы(ДеревоОбъект, "Дерево");
КонецПроцедуры

Вариант Б. Очистка строк напрямую в данных формы:

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


// Полная очистка строк дерева на форме
Дерево.ПолучитьЭлементы().Очистить();

// Пример обхода и заполнения первого уровня
Уровень1 = Дерево.ПолучитьЭлементы();
Для Каждого СтрДерева Из Уровень1 Цикл
    Уровень2 = СтрДерева.ПолучитьЭлементы();
    // Обработка подчиненных строк
КонецЦикла;

Решение 2. Динамическое создание структуры только при первом вызове

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


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

Решение 3. Полное удаление и пересоздание реквизитов и элементов

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

  1. Удаление элементов формы: Сначала удаляем визуальные элементы (поля колонок и саму таблицу) через Элементы.Удалить(). Если попытаться удалить реквизит формы, на который ссылается элемент интерфейса, возникнет исключение.
  2. Удаление реквизитов формы: Передаем имя удаляемого реквизита во второй параметр метода ИзменитьРеквизиты(). При удалении родительского реквизита Дерево платформа автоматически удаляет и его подчиненные колонки.

Посмотрим на пример кода корректного пересоздания структуры:


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

Таким образом, для устранения ошибки неуникальности имени достаточно либо исключить повторный вызов ИзменитьРеквизиты() при статичном наборе колонок, либо предварительно удалять старые реквизиты и элементы формы перед их повторным добавлением.

← На главную