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