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