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