При разработке в среде «1С:Предприятие 8» на управляемых формах (особенно при написании расширений конфигурации) программисты часто сталкиваются с задачей динамического построения интерфейса. Типичная ситуация: необходимо вывести на форму несколько полей ввода, количество и состав которых заранее неизвестны и определяются данными из информационной базы (например, записями из регистра сведений). Однако при попытке программно связать созданный элемент формы с данными разработчики регулярно получают ошибку «Неверное значение» в свойстве ПутьКДанным. Разберем подробно, почему возникает эта ошибка и как правильно реализовать подобную задачу.
Для начала проанализируем ситуацию, описанную автором темы. В его коде на сервере выполняется запрос к регистру сведений РегистрСведений.unbredWords, результат выгружается в таблицу значений, и в цикле совершается попытка добавить элементы формы:
НовыйЭлементФормы = Элементы.Добавить(строчка.Eng, Тип("ПолеФормы"));
НовыйЭлементФормы.Вид = ВидПоляФормы.ПолеВвода;
НовыйЭлементФормы.ПутьКДанным = "Строчка.Eng"; // Здесь происходит падение системы
Почему этот код принципиально неработоспособен? Дело в фундаментальном разделении управляемой формы на данные (реквизиты формы) и визуальное представление (элементы формы).
Свойство ПутьКДанным у элемента формы указывает платформе, из какого именно реквизита формы элемент должен считывать значение и куда записывать введенный пользователем текст. В качестве значения этого свойства может выступать только строковое представление пути к уже существующему реквизиту формы (например, "Объект.ИмяРеквизита" или созданному динамически реквизиту верхнего уровня).
В коде автора переменная строчка — это временный локальный объект в оперативной памяти сервера (строка таблицы значений). Платформа 1С не умеет связывать визуальные элементы интерфейса с локальными переменными процедур. Как только процедура завершит свою работу, переменная строчка уничтожится, а элемент формы останется ни с чем. Именно поэтому при попытке присвоить свойству ПутьКДанным строку, не являющуюся зарегистрированным реквизитом формы, система выдает ошибку «Неверное значение».
Вывод: чтобы вывести поле ввода на форму и связать его с данными, мы должны сначала программно создать сам Реквизит формы, и только затем создавать Элемент формы, указывая в качестве его пути к данным имя нового реквизита.
---
Разберем по шагам, как правильно решить задачу автора. Всю процедуру мы будем выполнять строго на сервере в обработчике события формы ПриСозданииНаСервере.
Сначала напишем запрос для выборки уникальных идентификаторов и значений из регистра сведений. Нам понадобятся данные для заголовков полей и их будущих значений.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| unbredWords.Eng КАК Eng,
| unbredWords.Ru КАК Ru
|ИЗ
| РегистрСведений.unbredWords КАК unbredWords";
ТЗ = Запрос.Выполнить().Выгрузить();
Для создания реквизитов формы используется объект РеквизитФормы и метод формы ИзменитьРеквизиты(). Обратите внимание, что метод ИзменитьРеквизиты() — это крайне ресурсоемкая операция, так как она приводит к полной пересборке структуры формы в памяти. Вызывать её в цикле для каждого реквизита категорически нельзя! Необходимо сначала собрать все добавляемые реквизиты в один массив, а затем передать его в метод один раз.
Имя динамического реквизита должно отвечать правилам именования переменных в 1С (не содержать пробелов, спецсимволов и не начинаться с цифры). Рекомендуется добавлять уникальный префикс, особенно при работе в расширении.
ДобавляемыеРеквизиты = Новый Массив;
Для Каждого Строчка Из ТЗ Цикл
// Формируем корректное и уникальное имя реквизита
ИмяРеквизита = "расш_ДинамическоеПоле_" + Строчка.Eng;
// Создаем описание типа данных, которые будут храниться в реквизите
МассивТипов = Новый Массив;
МассивТипов.Добавить(Тип("Строка"));
ОписаниеТиповСтрока = Новый ОписаниеТипов(МассивТипов);
// Инициализируем новый реквизит формы
НовыйРеквизит = Новый РеквизитФормы(
ИмяРеквизита, // Имя
ОписаниеТиповСтрока, // Тип
, // Путь к родителю (пусто для верхнего уровня формы)
Строчка.Eng, // Заголовок
Истина // Сохраняемые данные
);
ДобавляемыеРеквизиты.Добавить(НовыйРеквизит);
КонецЦикла;
// Добавляем реквизиты на форму одним вызовом
ЭтаФорма.ИзменитьРеквизиты(ДобавляемыеРеквизиты);
Теперь, когда под каждое поле в памяти формы выделено место (созданы реквизиты), мы можем приступить к созданию визуальных элементов и их заполнению значениями по умолчанию.
Для Каждого Строчка Из ТЗ Цикл
ИмяРеквизита = "расш_ДинамическоеПоле_" + Строчка.Eng;
ИмяЭлемента = "Элемент_" + Строчка.Eng;
// Создаем визуальное поле ввода на форме
НовыйЭлементФормы = Элементы.Добавить(ИмяЭлемента, Тип("ПолеФормы"));
НовыйЭлементФормы.Вид = ВидПоляФормы.ПолеВвода;
// Привязываем элемент к созданному ранее реквизиту формы
НовыйЭлементФормы.ПутьКДанным = ИмяРеквизита;
// Заполняем созданный реквизит формы значением из нашей таблицы
ЭтаФорма[ИмяРеквизита] = Строчка.Ru;
КонецЦикла;
---
Поскольку автор решает задачу внутри расширения, необходимо учитывать критически важные архитектурные правила платформы 1С:
расш_). Если разработчик основной конфигурации в очередном обновлении добавит реквизит с таким же именем, какое вы генерируете динамически, система выдаст критическую ошибку при открытии формы.ЭтаФорма.расш_Поле1 вызовет ошибку компиляции. Обращайтесь к ним исключительно через квадратные скобки: ЭтаФорма["расш_Поле1"].&После("ПриСозданииНаСервере") общего модуля или формы, что гарантирует высокую стабильность расширения при обновлениях базовой конфигурации.---
Проанализируем ситуацию с точки зрения юзабилити и производительности. Если в вашем регистре сведений unbredWords будет содержаться более 50–100 записей, создание сотен отдельных реквизитов и элементов превратит форму в бесконечно длинную «простыню». Это приведет к сильным зависаниям интерфейса при открытии формы на стороне клиента.
Гораздо более изящным решением является использование элемента формы типа Таблица, связанного с динамической таблицей значений.
Рассмотрим, как это реализовать:
ТаблицаЗначений (например, ТаблицаТестирования).СловоАнглийское (строка), Перевод (строка), ОтветПользователя (строка).ТаблицаФормы со всеми колонками. Колонку ОтветПользователя сделайте доступной для редактирования, а остальные — только для просмотра.При открытии формы вам останется лишь написать простой и быстрый код для заполнения таблицы:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| unbredWords.Eng КАК СловоАнглийское,
| unbredWords.Ru КАК Перевод
|ИЗ
| РегистрСведений.unbredWords КАК unbredWords";
РезультатЗапроса = Запрос.Выполнить();
Если Не РезультатЗапроса.Пустой() Тогда
// Загружаем данные напрямую в реквизит формы "ТаблицаТестирования"
ТаблицаТестирования.Загрузить(РезультатЗапроса.Выгрузить());
КонецЕсли;
КонецПроцедуры
Преимущества данного подхода очевидны:
ТаблицаТестирования и сравнить колонку Перевод с колонкой ОтветПользователя.