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

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

При разработке в среде «1С:Предприятие 8» на управляемых формах (особенно при написании расширений конфигурации) программисты часто сталкиваются с задачей динамического построения интерфейса. Типичная ситуация: необходимо вывести на форму несколько полей ввода, количество и состав которых заранее неизвестны и определяются данными из информационной базы (например, записями из регистра сведений). Однако при попытке программно связать созданный элемент формы с данными разработчики регулярно получают ошибку «Неверное значение» в свойстве ПутьКДанным. Разберем подробно, почему возникает эта ошибка и как правильно реализовать подобную задачу.

Выясняем причину ошибки: архитектура управляемых форм

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


НовыйЭлементФормы = Элементы.Добавить(строчка.Eng, Тип("ПолеФормы"));
НовыйЭлементФормы.Вид = ВидПоляФормы.ПолеВвода;
НовыйЭлементФормы.ПутьКДанным = "Строчка.Eng"; // Здесь происходит падение системы

Почему этот код принципиально неработоспособен? Дело в фундаментальном разделении управляемой формы на данные (реквизиты формы) и визуальное представление (элементы формы).

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

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

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

---

Решение 1: Пошаговый алгоритм динамического создания реквизитов и элементов

Разберем по шагам, как правильно решить задачу автора. Всю процедуру мы будем выполнять строго на сервере в обработчике события формы ПриСозданииНаСервере.

Шаг 1. Получение данных для построения интерфейса

Сначала напишем запрос для выборки уникальных идентификаторов и значений из регистра сведений. Нам понадобятся данные для заголовков полей и их будущих значений.


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	unbredWords.Eng КАК Eng,
    |	unbredWords.Ru КАК Ru
    |ИЗ
    |	РегистрСведений.unbredWords КАК unbredWords";
ТЗ = Запрос.Выполнить().Выгрузить();

Шаг 2. Программное создание реквизитов формы

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

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


ДобавляемыеРеквизиты = Новый Массив;

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

// Добавляем реквизиты на форму одним вызовом
ЭтаФорма.ИзменитьРеквизиты(ДобавляемыеРеквизиты);

Шаг 3. Программное создание элементов формы и привязка к данным

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


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

---

Особенности разработки в расширениях конфигурации

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

  1. Использование префиксов: Всегда называйте свои динамические реквизиты и элементы с уникальным префиксом вашего расширения (например, расш_). Если разработчик основной конфигурации в очередном обновлении добавит реквизит с таким же именем, какое вы генерируете динамически, система выдаст критическую ошибку при открытии формы.
  2. Обращение к реквизитам в коде: Конфигуратор на этапе компиляции не знает о существовании динамических реквизитов. Поэтому обращение к ним вида ЭтаФорма.расш_Поле1 вызовет ошибку компиляции. Обращайтесь к ним исключительно через квадратные скобки: ЭтаФорма["расш_Поле1"].
  3. Отказ от заимствования форм: Одно из главных преимуществ программного создания элементов — это возможность не переносить (не заимствовать) саму форму в расширение. Вы можете написать этот код в обработчике &После("ПриСозданииНаСервере") общего модуля или формы, что гарантирует высокую стабильность расширения при обновлениях базовой конфигурации.

---

Решение 2: Альтернативный профессиональный подход (Таблица значений)

Проанализируем ситуацию с точки зрения юзабилити и производительности. Если в вашем регистре сведений unbredWords будет содержаться более 50–100 записей, создание сотен отдельных реквизитов и элементов превратит форму в бесконечно длинную «простыню». Это приведет к сильным зависаниям интерфейса при открытии формы на стороне клиента.

Гораздо более изящным решением является использование элемента формы типа Таблица, связанного с динамической таблицей значений.

Рассмотрим, как это реализовать:

  1. Создайте на форме в конфигураторе (или расширении) реквизит с типом ТаблицаЗначений (например, ТаблицаТестирования).
  2. Добавьте в эту таблицу колонки: СловоАнглийское (строка), Перевод (строка), ОтветПользователя (строка).
  3. Перетащите этот реквизит на форму, чтобы платформа автоматически сгенерировала элемент интерфейса типа ТаблицаФормы со всеми колонками. Колонку ОтветПользователя сделайте доступной для редактирования, а остальные — только для просмотра.

При открытии формы вам останется лишь написать простой и быстрый код для заполнения таблицы:


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |	unbredWords.Eng КАК СловоАнглийское,
        |	unbredWords.Ru КАК Перевод
        |ИЗ
        |	РегистрСведений.unbredWords КАК unbredWords";
    
    РезультатЗапроса = Запрос.Выполнить();
    Если Не РезультатЗапроса.Пустой() Тогда
        // Загружаем данные напрямую в реквизит формы "ТаблицаТестирования"
        ТаблицаТестирования.Загрузить(РезультатЗапроса.Выгрузить());
    КонецЕсли;
    
КонецПроцедуры

Преимущества данного подхода очевидны:

← На главную