При переходе с платформы 1С:Предприятие 8.2 на версию 8.3 (особенно при работе через веб-интерфейс) разработчики часто сталкиваются с критической ошибкой: «Использование модальных окон в данном режиме запрещено». Эта проблема блокирует работу пользователей и требует пересмотра логики взаимодействия программы с человеком. В данной статье мы детально разберем, почему возникает эта ошибка, и научимся переписывать старый код под современные стандарты асинхронности.
Давайте проанализируем причину изменений. Традиционные модальные окна (функции Вопрос(), Предупреждение(), ОткрытьФормуМодально()) полностью останавливают выполнение программного кода до тех пор, пока пользователь не нажмет какую-либо кнопку. Однако современные веб-браузеры (Chrome, Firefox, Safari) и мобильные платформы больше не поддерживают такую блокировку.
Для обеспечения стабильной работы в браузере фирма «1С» внедрила асинхронную модель. В этой модели программа не ждет ответа пользователя, «замирая» на строке кода, а «засыпает», предварительно указав, какую процедуру нужно выполнить, когда пользователь сделает свой выбор. Это требует от нас, разработчиков, изменения архитектуры процедур.
Рассмотрим основной способ решения проблемы, который является стандартным для версий 8.3.11 и выше. Вместо функции Вопрос(), которая возвращает результат немедленно, мы будем использовать процедуру ПоказатьВопрос().
Разберем ситуацию на примере из темы форума. Исходный (нерабочий в вебе) код выглядел так:
Процедура ПриОткрытии(Отказ)
// ... получение курса ...
Ответ = Вопрос("Установлен неверный рабочий курс. Заменить?", Режим);
Если Ответ = КодВозвратаДиалога.Да Тогда
Объект.КурсРабочий = КурсРабочий;
КонецЕсли;
КонецПроцедуры
Чтобы этот код заработал в веб-клиенте, нам нужно разделить его на две части: инициирующую и обрабатывающую. Для этого используется объект ОписаниеОповещения. Проанализируем правильный алгоритм реализации:
ОписаниеОповещения.Посмотрим на исправленный пример кода:
&НаКлиенте
Процедура ПриОткрытии(Отказ)
КурсРабочий = События_Каштан.ПолучитьТекущийРабочийКурс(КонецДня(Объект.Дата));
Если Не ЗначениеЗаполнено(Объект.КурсРабочий) Тогда
Объект.КурсРабочий = КурсРабочий;
ИначеЕсли Объект.КурсРабочий <> КурсРабочий Тогда
// 1. Подготавливаем описание оповещения
// "ПослеЗакрытияВопроса" - имя процедуры, которая выполнится потом
// ЭтаФорма - где искать эту процедуру
Оповещение = Новый ОписаниеОповещения("ПослеЗакрытияВопроса", ЭтаФорма, КурсРабочий);
// 2. Вызываем немодальный диалог
Режим = РежимДиалогаВопрос.ДаНет;
ПоказатьВопрос(Оповещение, "Установлен неверный рабочий курс. Заменить?", Режим);
// ВНИМАНИЕ: Код после ПоказатьВопрос продолжит выполняться сразу же,
// не дожидаясь ответа пользователя!
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ПослеЗакрытияВопроса(Результат, ДополнительныеПараметры) Экспорт
// Результат - это то, что нажал пользователь (Да, Нет и т.д.)
// ДополнительныеПараметры - это наш КурсРабочий, переданный выше
Если Результат = КодВозвратаДиалога.Да Тогда
Объект.КурсРабочий = ДополнительныеПараметры;
// Здесь можно вызвать дополнительные действия, например, оповестить пользователя
КонецЕсли;
КонецПроцедуры
Важный нюанс: Обработчик оповещения (в нашем примере ПослеЗакрытияВопроса) обязательно должен быть помечен ключевым словом Экспорт, иначе платформа не сможет вызвать его из внутреннего механизма диалога.
В ходе обсуждения на форуме было замечено, что получение курса в процедуре ПриОткрытии может приводить к лишним вызовам сервера. Выясним причину: ПриОткрытии работает на клиенте, а получение курса обычно требует обращения к базе данных (на сервер).
Для оптимизации рекомендуется перенести получение данных в процедуру ПриСозданииНаСервере. Это позволит подготовить все данные еще до того, как форма отобразится у пользователя. Однако помните, что задавать вопросы пользователю (вызывать диалоги) на сервере нельзя — это можно делать только в клиентских процедурах типа ПриОткрытии или ПослеЗаписи.
Если проект огромный и переписывать все вызовы Вопрос() прямо сейчас нет возможности, существует «костыль», который поможет запустить систему в тонком клиенте (но не всегда в веб-клиенте).
В конфигураторе выделите корень дерева метаданных (вашу конфигурацию), нажмите правую кнопку мыши и выберите «Свойства». Найдите параметр «Режим использования модальности». Если установить его в значение «Использовать», система перестанет выдавать ошибку в тонком клиенте. Но будьте осторожны: в веб-интерфейсе браузер всё равно может заблокировать окно, и пользователь просто ничего не увидит. Поэтому единственный надежный путь — это рефакторинг кода.
Если ваша платформа выше версии 8.3.18, вы можете использовать еще более элегантный механизм — Асинх и Ждать. Это позволяет писать асинхронный код так, будто он синхронный. Проанализируем, как бы выглядел наш пример в современном стиле:
&НаКлиенте
Асинх Процедура ПриОткрытии(Отказ)
// ... логика проверки курса ...
Режим = РежимДиалогаВопрос.ДаНет;
// Используем Ждать и Асинх-версию метода
Ответ = Ждать ВопросАсинх("Установлен неверный рабочий курс. Заменить?", Режим);
Если Ответ = КодВозвратаДиалога.Да Тогда
Объект.КурсРабочий = КурсРабочий;
КонецЕсли;
КонецПроцедуры
Этот подход значительно упрощает чтение кода, так как нам не нужно создавать отдельную процедуру Экспорт и передавать параметры через ОписаниеОповещения. Система сама приостановит выполнение процедуры на строке с Ждать и возобновит её после ответа пользователя.
Чтобы не ждать, пока пользователи наткнутся на ошибки в веб-интерфейсе, проанализируем конфигурацию заранее. В Конфигураторе выберите пункт меню «Конфигурация» — «Проверка конфигурации». В открывшемся окне установите флажок «Проверка логической целостности» и обязательно — «Поиск использования модальных методов». После проверки 1С выдаст вам полный список строк кода, где используются запрещенные в вебе Вопрос, Предупреждение и другие методы.
Подведем итоги: для стабильной работы в веб-интерфейсе необходимо полностью отказаться от синхронных диалогов. Использование ОписаниеОповещения и метода ПоказатьВопрос() является базовым навыком современного разработчика 1С, гарантирующим работоспособность приложения в любых браузерах и на мобильных устройствах.