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