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