Как обновить реквизит (представление ссылки) на управляемой форме в 1С при изменении объекта на сервере?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке на платформе 1С:Предприятие 8.3 в режиме управляемого приложения разработчики часто сталкиваются со следующей проблемой: на управляемой форме расположен реквизит ссылочного типа (например, ссылка на справочник Номенклатура или ТМЦ). Программно на сервере мы получаем объект по этой ссылке, изменяем его текстовые свойства (например, наименование), записываем изменения в базу данных, но на форме пользователя по-прежнему отображается старое наименование. При этом интерактивное обновление формы, её закрытие и повторное открытие или вызов стандартных методов обновления отображения не приносят результата.

В этой статье мы подробно разберем, почему возникает этот эффект, как работает клиентское кэширование в платформе 1С, и рассмотрим несколько эффективных и правильных способов программного обновления измененных данных на форме.

Выясняем причину: почему платформа кэширует данные?

Чтобы понять, почему изменения не отображаются на форме мгновенно, проанализируем архитектуру работы управляемого клиента. Платформа 1С проектировалась с расчетом на работу в веб-клиенте и по низкоскоростным каналам связи. Для минимизации количества сетевых запросов (так называемых round-trips) между клиентом и сервером применяется агрессивное клиентское кэширование данных ссылочного типа.

Когда управляемая форма отображает поле со ссылкой (например, СправочникСсылка.Номенклатура), клиентское приложение не запрашивает информацию из базы данных каждый раз при перерисовке интерфейса. Платформа единожды получает представление ссылки и кэширует его на клиенте. В этот кэш попадают:

Когда мы получаем объект на сервере через метод ПолучитьОбъект(), изменяем его и вызываем метод Записать(), эти действия происходят на уровне СУБД и сервера приложений 1С. Клиентский сеанс, в котором открыта форма, ничего не знает о том, что в базе данных изменилась запись, соответствующая нашей ссылке. Для клиента кэш по-прежнему считается валидным, поэтому на форме продолжает отображаться старое наименование до тех пор, пока кэш не будет принудительно очищен или пока не завершится сеанс работы пользователя.

Разбор исходной ситуации (проблемный код)

Рассмотрим код, который изначально привел к зависанию неактуальных данных на форме:


&НаСервере
Процедура ОбновитьСправочник()
    НоменклатураОбъект = Объект.Номенклатура.ПолучитьОбъект();
    НоменклатураОбъект.Наименование = НоменклатураОбъект.Наименование + "1";
    НоменклатураОбъект.Записать();
КонецПроцедуры

&НаКлиенте
Процедура Команда1(Команда)
    ОбновитьСправочник();
КонецПроцедуры

При выполнении этого обработчика наименование элемента в базе данных успешно изменилось (к нему добавилась единица), однако поле ввода на форме по-прежнему содержит старую строчку. Вызов стандартных методов обновления или переоткрытие формы не помогают, так как кэш представлений является глобальным для клиентского приложения и не очищается при простых манипуляциях с формой.

Ниже мы разберем три рабочих сценария решения этой проблемы в зависимости от контекста вашей задачи.

Решение №1: Использование метода ОтобразитьИзменениеДанных()

Наиболее точечным и быстрым решением в рамках одной управляемой формы является использование метода управляемой формы ОтобразитьИзменениеДанных(). Этот метод сообщает платформе, что определенные данные в базе данных были изменены, и указывает клиенту на необходимость очистить локальный кэш для конкретной ссылки.

Изменим наш код следующим образом:


&НаСервере
Процедура ОбновитьСправочникНаСервере()
    НоменклатураОбъект = Объект.Номенклатура.ПолучитьОбъект();
    НоменклатураОбъект.Наименование = НоменклатураОбъект.Наименование + "1";
    НоменклатураОбъект.Записать();
КонецПроцедуры

&НаКлиенте
Процедура Команда1(Команда)
    // 1. Изменяем данные в базе данных на сервере
    ОбновитьСправочникНаСервере();
    
    // 2. Оповещаем форму о необходимости сбросить кэш представлений для измененного объекта
    ЭтаФорма.ОтобразитьИзменениеДанных(Объект.Номенклатура, ВидИзмененияДанных.Изменение);
КонецПроцедуры

Как это работает: Метод ОтобразитьИзменениеДанных() принимает два параметра: ссылку на измененный объект базы данных и системное перечисление ВидИзмененияДанных (в данном случае мы используем значение ВидИзмененияДанных.Изменение). При вызове этого метода на клиенте происходит мгновенная очистка локальных кэшей для переданной ссылки. В результате элементы формы, отображающие эту ссылку, тут же отправляют легкий запрос на сервер для получения нового актуального представления, и пользователь видит обновленные данные без перезапуска программы.

Решение №2: Использование глобального метода ОповеститьОбИзменении()

Метод ОтобразитьИзменениеДанных() отлично работает локально, но у него есть ограничение: он обновляет представление ссылки только в контексте текущей управляемой формы. Если у пользователя открыто несколько окон (например, форма списка номенклатуры, форма выбора и форма документа, где выбрана эта номенклатура), изменения отобразятся только там, где был вызван метод.

Для глобального решения проблемы кэширования в рамках всего клиентского сеанса используется метод глобального контекста ОповеститьОбИзменении(). Давайте посмотрим, как его применить:


&НаКлиенте
Процедура Команда1(Команда)
    // Выполняем запись изменений на сервере
    ОбновитьСправочникНаСервере();
    
    // Глобальное оповещение системы об изменении объекта
    ОповеститьОбИзменении(Объект.Номенклатура);
КонецПроцедуры

Преимущества метода ОповеститьОбИзменении():

  1. Глобальный масштаб: Очистка кэша представлений происходит во всех открытых формах текущего сеанса пользователя.
  2. Обновление динамических списков: Динамические списки, содержащие измененный объект (например, списки справочников или документов), автоматически перечитают данные измененной строки без необходимости полного интерактивного обновления списка.
  3. Работа с Полями ввода: В отличие от локальных методов, ОповеститьОбИзменении() гарантированно обновляет представление не только в полях вида Поле надписи, но и внутри активных элементов управления вида Поле ввода.

Решение №3: Использование Библиотеки стандартных подсистем (БСП)

Если вы ведете разработку в современной типовой конфигурации 1С (такой как 1С:ERP, 1С:Комплексная автоматизация, 1С:Управление торговлей или 1С:Бухгалтерия), то в вашем распоряжении находится встроенный функционал Библиотеки стандартных подсистем (БСП). Разработчики БСП создали удобную обертку для группового обновления кэша.

Вместо вызова низкоуровневых методов платформы рекомендуется использовать функции общего модуля БСП:


&НаКлиенте
Процедура Команда1(Команда)
    ОбновитьСправочникНаСервере();
    
    // Подготовка массива измененных ссылок
    МассивСсылок = Новый Массив;
    МассивСсылок.Добавить(Объект.Номенклатура);
    
    // Использование стандартного метода БСП для оповещения
    ОбщегоНазначенияКлиент.ОповеститьОбИзмененииОбъектов(МассивСсылок);
КонецПроцедуры

Этот подход делает код более стандартизированным и защищенным от возможных изменений в поведении платформы при будущих обновлениях.

Типичные ошибки: почему ОбновитьОтображениеДанных() бесполезен в данном случае?

Очень распространенной ошибкой среди начинающих (а иногда и опытных) разработчиков является попытка решить проблему вызовом метода ЭтаФорма.ОбновитьОтображениеДанных(). Давайте разберем, почему это не работает.

Метод ОбновитьОтображениеДанных() предназначен исключительно для принудительного обновления визуального состояния элементов формы на клиенте. Он заставляет платформу заново рассчитать:

При этом ОбновитьОтображениеДанных() не очищает кэш представлений ссылок и не делает никаких запросов к серверу базы данных для перечитывания информации. Пытаться обновить изменившееся наименование номенклатуры с его помощью абсолютно бесполезно.

Разделение сценариев: Изменение внешнего объекта vs Изменение основного реквизита формы

Для построения качественной архитектуры приложения важно четко разграничивать два сценария работы с данными формы на сервере:

Сценарий А: Изменение стороннего объекта (как в нашей теме)

Мы меняем объект, ссылка на который хранится в реквизите текущей формы (например, меняем реквизиты элемента справочника Номенклатура из формы документа). Сам документ при этом не перезаписывается. В этом сценарии изменения происходят в другой таблице базы данных. Единственный способ обновить представление на форме — это сбросить кэш представлений методами ОтобразитьИзменениеДанных() или ОповеститьОбИзменении().

Сценарий Б: Изменение самого редактируемого объекта формы

Мы хотим изменить реквизиты объекта, который в данный момент редактируется на форме (например, программно заполнить табличную часть текущего документа на сервере). В этом случае использовать получение объекта через ссылку (ПолучитьОбъект()) крайне не рекомендуется, так как это приведет к конфликту блокировок данных.

Для Сценария Б правильным решением является конвертация реквизита управляемой формы в объект и обратно с помощью специальных методов контекста сервера:


&НаСервере
Процедура ЗаполнитьРеквизитыТекущегоОбъектаНаСервере()
    // Конвертируем данные формы в прикладной объект для манипуляций на сервере
    ДокументОбъект = РеквизитФормыВЗначение("Объект");
    
    // Выполняем необходимые изменения данных
    ДокументОбъект.ЗаполнитьТабличнуюЧасть();
    
    // Возвращаем измененный объект обратно в контекст управляемой формы
    ЗначениеВРеквизитФормы(ДокументОбъект, "Объект");
КонецПроцедуры

Этот метод полностью синхронизирует состояние экранной формы с измененными данными объекта, и вызов методов очистки кэша представлений в данном сценарии не требуется.

Резюме и рекомендации

Подведем итоги нашего разбора:

  1. Если вы изменили данные объекта в базе данных на сервере напрямую через ПолучитьОбъект().Записать(), клиентский кэш представлений ссылок не обновится автоматически.
  2. Для точечного обновления представления конкретной ссылки на текущей управляемой форме используйте метод ЭтаФорма.ОтобразитьИзменениеДанных(Ссылка, ВидИзмененияДанных.Изменение).
  3. Для глобального обновления представления ссылки во всех открытых окнах сеанса и динамических списках используйте метод глобального контекста ОповеститьОбИзменении(Ссылка).
  4. Если конфигурация построена на БСП, применяйте метод ОбщегоНазначенияКлиент.ОповеститьОбИзмененииОбъектов(МассивСсылок).
  5. Не путайте методы обновления данных с методом ОбновитьОтображениеДанных(), который отвечает только за пересчет визуального оформления интерфейса.
← На главную