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