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