В разработке на платформе 1С:Предприятие 8.3 часто возникают ситуации, когда необходимо точно знать, происходил ли контекстный серверный вызов между двумя событиями на клиенте. Это критично при работе со сложными объектами, такими как ПолеHTMLДокумента, внешние компоненты или кэшированные данные, которые могут «протухнуть» или сброситься после того, как форма побывала на сервере. Рассмотрим механизмы работы управляемых форм и разберем элегантный способ решения этой задачи.
Разберем, почему состояние формы может измениться. Управляемая форма существует в двух ипостасях: на клиенте и на сервере. Когда мы выполняем контекстный серверный вызов (процедура с директивой &НаСервере), платформа выполняет следующие действия:
Важно понимать: при каждом таком вызове раздел инициализации модуля управляемой формы (код, который написан вне процедур и функций) выполняется заново на стороне сервера. Именно эту особенность мы проанализируем для решения нашей задачи.
Самый простой и универсальный способ отследить контекстный вызов — завести реквизит формы, который будет инкрементироваться (увеличиваться) автоматически при каждом «посещении» сервера. Рассмотрим, как это реализовать по шагам.
Во-первых, создадим в реквизитах формы переменную СчетчикСерверныхВызовов с типом Число. Во-вторых, в самом конце модуля формы, вне каких-либо процедур, разместим следующий код:
#Если Сервер Тогда
СчетчикСерверныхВызовов = СчетчикСерверныхВызовов + 1;
#КонецЕсли
Проанализируем ситуацию: когда форма открывается или вызывается контекстно на сервере, препроцессор видит директиву #Если Сервер и выполняет инкремент. На клиенте этот код игнорируется. Таким образом, если значение реквизита изменилось по сравнению с предыдущим замером на клиенте — значит, контекстный вызов гарантированно состоялся.
Представим, что у нас есть клиентская процедура, в которой нам нужно проверять актуальность закэшированных данных. Посмотрим на пример кода:
&НаКлиенте
Переменная МойКэш; // Переменная модуля для хранения объекта
&НаКлиенте
Процедура ВыполнитьДействие()
// Проверяем, не изменился ли счетчик с момента последнего сохранения кэша
Если ТекущееЗначениеСчетчика <> СчетчикСерверныхВызовов Тогда
// Был серверный вызов! Кэш может быть неактуален.
ОбновитьКэш();
ТекущееЗначениеСчетчика = СчетчикСерверныхВызовов;
КонецЕсли;
// Продолжаем работу с актуальными данными
...
КонецПроцедуры
В данном примере ТекущееЗначениеСчетчика — это переменная модуля (на клиенте), в которой мы запоминаем последнее состояние нашего серверного счетчика.
Иногда серверные вызовы происходят неявно для разработчика. Например, изменение некоторых свойств элементов формы на клиенте может вызвать автоматическое обращение к серверу для пересчета оформления или проверки данных. Выясним причину: такие свойства, как ВысотаПодвала у таблицы или изменение заголовков некоторых элементов, требуют синхронизации с серверной моделью.
Рассмотрим пример неявного вызова:
&НаКлиенте
Процедура ИзменитьОформление()
// Это действие может инициировать неявный контекстный вызов на сервер
Элементы.СписокНоменклатуры.ВысотаПодвала = 2;
КонецПроцедуры
Даже в этом случае наш метод со счетчиком в секции #Если Сервер Тогда сработает корректно, так как платформа будет вынуждена инициализировать модуль на стороне сервера для обработки изменений формы.
Разберем реальный кейс, связанный с использованием WebKit/Chromium в 1С. Часто разработчики сохраняют ссылку на окно браузера или конкретный DOM-объект в переменную модуля:
Браузер = Элементы.ПолеHTML.Документ.defaultView;
После любого контекстного серверного вызова объект defaultView (или сам Документ) часто становится невалидным («протухает»). Это происходит из-за того, что при возврате с сервера платформа перерисовывает/переинициализирует контейнер HTML-поля, и старые ссылки в памяти клиента начинают указывать на уничтоженные объекты. Попытка обращения к такой переменной часто приводит к аварийному завершению работы программы (Access Violation).
Используя предложенный метод со счетчиком, мы можем вовремя заметить факт серверного вызова и, не обращаясь к «испорченной» переменной, заново получить живую ссылку на объект браузера. Это позволяет избежать критических ошибок и значительно повысить стабильность приложения.
Нужно учитывать следующие моменты при работе с этим методом:
&НаСервереБезКонтекста не вызывает переинициализацию модуля формы. Счетчик не изменится. Однако внеконтекстные вызовы и не портят клиентские объекты формы, так как данные формы на сервер не передаются.Проанализировав ситуацию, мы видим, что предложенный подход является наиболее надежным и простым в реализации для управления состоянием управляемой формы в сложных сценариях взаимодействия клиента и сервера.