При проектировании динамических интерфейсов в 1С:Предприятии часто возникает классическая задача: при выборе строки в одной табличной части необходимо мгновенно получить детальные данные (например, состав заказа) из внешней информационной системы или веб-сервиса и отобразить их во второй табличной части. Стандартным механизмом для отслеживания перемещения курсора является событие таблицы ПриАктивизацииСтроки. Однако неосторожное программирование в этом обработчике мгновенно приводит к зависанию интерфейса и бесконечному зацикливанию приложения. Выясним причину этой проблемы и разберем по шагам, как реализовать стабильное и производительное решение.
Проанализируем механику взаимодействия платформы 1С и клиентского приложения. Событие ПриАктивизацииСтроки генерируется на клиенте каждый раз, когда меняется активная строка таблицы. Это происходит не только при физическом клике пользователя мышкой или нажатии стрелок на клавиатуре, но и при любом программном обновлении элементов формы, связанных с этой таблицей.
Когда мы выполняем контекстный серверный вызов (процедура с директивой &НаСервере) непосредственно внутри этого обработчика и в процессе работы модифицируем реквизиты формы (например, загружаем новые строки в подчиненную табличную часть с помощью метода Загрузить()), происходит следующее:
ПриАктивизацииСтроки.ПриАктивизацииСтроки для основной таблицы.Главное правило разработки управляемых форм: в обработчике события ПриАктивизацииСтроки категорически запрещено использовать контекстные серверные вызовы, изменяющие данные формы.
---
Для создания отказоустойчивого интерфейса мы применим архитектурное решение, состоящее из трех ключевых правил:
&НаСервереБезКонтекста. Они не передают на сервер тяжелый контекст всей формы и не инициируют автоматическое обновление её элементов при возврате.---
Рассмотрим детально, как написать правильный и безопасный код в модуле управляемой формы.
Поскольку системный метод ПодключитьОбработчикОжидания не поддерживает передачу параметров в вызываемую процедуру, нам необходимо сохранить уникальный идентификатор текущей строки во временной переменной модуля формы на клиенте. Объявим переменные в самом верху модуля:
&НаКлиенте
Перем ТекущийИдентификаторЗаказа; // Хранит ИД строки, которую выделил пользователь
&НаКлиенте
Перем ЗагруженныйИдентификаторЗаказа; // Хранит ИД строки, данные которой уже отображены на форме
В обработчике события мы сбрасываем ранее подключенный таймер, обновляем значение переменной с идентификатором строки и заново взводим таймер ожидания на 0.4 секунды. Это гарантирует, что запрос к веб-сервису уйдет только тогда, когда пользователь остановит навигацию по списку.
&НаКлиенте
Процедура ЗаказыКлиентовПриАктивизацииСтроки(Элемент)
// Отключаем предыдущий обработчик ожидания (сброс таймера)
ОтключитьОбработчикОжидания("ВыполнитьЗагрузкуСоставаЗаказа");
ТекДанные = Элементы.ЗаказыКлиентов.ТекущиеДанные;
Если ТекДанные = Неопределено Тогда
Возврат;
КонецЕсли;
// Запоминаем идентификатор текущей строки для последующей обработки
ТекущийИдентификаторЗаказа = ТекДанные.УникальныйИдентификаторЗаказа;
// Подключаем однократный обработчик ожидания с задержкой 0.4 секунды
ПодключитьОбработчикОжидания("ВыполнитьЗагрузкуСоставаЗаказа", 0.4, Истина);
КонецПроцедуры
Эта процедура выполняется в клиентском контексте, когда таймер отсчитал положенное время. Здесь мы выполняем проверку на кэширование, вызываем бесконтекстный сервер для получения данных от веб-сервиса и загружаем результат в реквизит формы.
&НаКлиенте
Процедура ВыполнитьЗагрузкуСоставаЗаказа()
// Если данные для этой строки уже загружены, ничего не делаем
Если ТекущийИдентификаторЗаказа = ЗагруженныйИдентификаторЗаказа Тогда
Возврат;
КонецЕсли;
// Вызываем бесконтекстную серверную функцию для получения данных из веб-сервиса
ДанныеДляЗаполнения = ПолучитьДанныеИзВебСервисаНаСервере(ТекущийИдентификаторЗаказа);
Если ДанныеДляЗаполнения <> Неопределено Тогда
// Быстрое заполнение табличной части на клиенте через работу с коллекцией реквизита формы
Объект.СодержаниеЗаказа.Очистить();
Для Каждого СтрокаТЗ Из ДанныеДляЗаполнения Цикл
НоваяСтрока = Объект.СодержаниеЗаказа.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаТЗ);
КонецЦикла;
// Фиксируем успешную загрузку
ЗагруженныйИдентификаторЗаказа = ТекущийИдентификаторЗаказа;
КонецЕсли;
КонецПроцедуры
Для выполнения интеграционного запроса мы создаем функцию с директивой &НаСервереБезКонтекста. Она выполняет вызов веб-сервиса, десериализацию данных и возвращает готовую таблицу значений на клиент. Обратите внимание, что мы не обращаемся к элементам формы напрямую внутри этой процедуры.
&НаСервереБезКонтекста
Функция ПолучитьДанныеИзВебСервисаНаСервере(Знач УникальныйИдентификаторЗаказа)
Попытка
// Инициализируем WS-прокси
Соединение = WSСсылки.SelectNom.СоздатьWSПрокси("SelectNom", "SelectNom", "SelectNomSoap");
// Выполняем вызов веб-сервиса
ОтветВебСервиса = Соединение.SelectNom(УникальныйИдентификаторЗаказа);
// Десериализуем XML/JSON ответ во временную Таблицу Значений
ТаблицаРезультат = ДесериализоватьОтвет(ОтветВебСервиса);
Возврат ТаблицаРезультат;
Исключение
// Логируем ошибку, чтобы не прерывать работу пользователя аварийно
ЗаписьЖурналаРегистрации("Интеграция.ВебСервис",
УровеньЖурналаРегистрации.Ошибка,,,
ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
Возврат Неопределено;
КонецПопытки;
КонецФункции
---
Описанный выше вариант с обработчиком ожидания отлично защищает форму от зацикливания и снижает нагрузку на базу данных при быстром скроллинге. Однако, если веб-сервис внешней системы работает нестабильно или долго отвечает (более 1–2 секунд), синхронный бесконтекстный вызов все равно заставит интерфейс пользователя «замереть» на время выполнения сетевого запроса.
Для создания по-настоящему профессионального решения рекомендуется использовать механизм асинхронных длительных операций на базе БСП (Библиотеки Стандартных Подсистем):
ВыполнитьЗагрузкуСоставаЗаказа вместо прямого вызова веб-сервиса мы инициируем фоновое задание с помощью метода ДлительныеОперации.ЗапуститьВыполнение().ДлительныеОперации.ОжидатьУправление()).Применение связки «Антидребезг + Безконтекстный вызов + Фоновое задание» является золотым стандартом разработки на платформе 1С:Предприятие 8.3 для любых задач интеграции в динамических формах.