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