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