При разработке интерактивных интерфейсов в среде 1С:Предприятие 8.3 программисты часто сталкиваются с задачей динамического обновления данных на форме. Классический пример — вывод актуальных остатков товаров на складе, курсов валют или статусов заказов в режиме реального времени. Для этого традиционно используется обработчик ожидания, который регулярно вызывает серверную процедуру обновления.
Однако здесь разработчиков подстерегает крайне неприятный эффект: в момент срабатывания таймера и обновления данных у пользователя «слетает» активный ввод. Открытые калькуляторы в числовых полях закрываются, выпадающие списки выбора мгновенно сворачиваются, а недописанный текст в полях ввода очищается или выделяется, заставляя пользователя начинать ввод заново. Выясним причину этой проблемы и подробно разберем несколько эффективных способов борьбы с ней.
Рассмотрим детально механику взаимодействия клиента и сервера в управляемом приложении. Когда срабатывает подключенный обработчик ожидания (например, через метод ПодключитьОбработчикОжидания с интервалом в 5 секунд), управление передается серверной процедуре. Если эта процедура объявлена с директивой &НаСервере (то есть выполняется в контексте формы), происходит следующее:
С точки зрения платформы, любое незавершенное действие пользователя в момент обновления (открытое окно встроенного калькулятора, развернутый календарь выбора даты, незавершенный набор строки, не подтвержденный нажатием клавиши Enter) считается временным, не зафиксированным в данных формы состоянием. При перерисовке элементов все эти временные объекты уничтожаются, фокус принудительно возвращается к активному элементу, а недописанный текст сбрасывается к исходному значению реквизита.
Проанализируем практические подходы к решению этой проблемы — от самых простых настроек интерфейса до глубоких архитектурных изменений.
Самый быстрый способ локально победить стирание вводимого текста в простых полях (строках, числах, датах) — изменить настройки самого элемента формы. По умолчанию у элементов ввода свойство ОбновлениеТекстаРедактирования установлено в значение Авто.
Если мы заменим его на значение При изменении значения, платформа изменит свое поведение:
Важное ограничение: этот способ отлично защищает от потери набранного текста, но он абсолютно бессилен перед проблемой закрытия калькуляторов, календарей и выпадающих списков автоподбора. Если пользователь открыл калькулятор и в этот момент сработал таймер — окно расчетов все равно закроется.
Если ваша задача — обновлять таблицу остатков или список документов, использование табличной части или таблицы значений на форме является архитектурной ошибкой. Гораздо более надежный и профессиональный подход — использование объекта ДинамическийСписок.
Рассмотрим, почему динамический список решает проблему:
Обновить(), а также встроенное свойство ПериодАвтообновления.Для реализации этого метода достаточно заменить реквизит формы типа ТаблицаЗначений на ДинамическийСписок с произвольным запросом к регистру остатков, вывести его на форму и настроить период автообновления.
Если отказаться от таблицы значений невозможно, мы можем оптимизировать серверные вызовы. Вместо контекстного вызова &НаСервере нам необходимо использовать вызов с директивой &НаСервереБезКонтекста.
При бесконтекстном вызове клиент не передает на сервер состояние всей формы, а сервер не возвращает ее обратно. Следовательно, платформа не перерисовывает элементы формы автоматически, и интерфейс пользователя не «дергается».
Разберем по шагам, как реализовать такое обновление на примере таблицы остатков:
&НаКлиенте
Процедура ОбработчикТиковСекунд()
// Получаем данные с сервера без передачи контекста формы
НовыеДанныеОстатков = ПолучитьОстаткиНаСервереБезКонтекста();
// Аккуратно обновляем только конкретные строки таблицы на клиенте,
// не затрагивая фокус ввода в других полях формы
ОбновитьТаблицуНаКлиенте(НовыеДанныеОстатков);
КонецПроцедуры
&НаСервереБезКонтекста
Функция ПолучитьОстаткиНаСервереБезКонтекста()
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ТоварыНаСкладахОстатки.Номенклатура КАК Номенклатура,
| ТоварыНаСкладахОстатки.ВНаличииОстаток КАК Остаток
|ИЗ
| РегистрНакопления.ТоварыНаСкладах.Остатки КАК ТоварыНаСкладахОстатки";
ТаблицаРезультата = Запрос.Выполнить().Выгрузить();
// Передаем данные на клиент в виде массива структур, так как ТаблицаЗначений не передается напрямую
МассивРезультата = Новый Массив;
Для Каждого Строка Из ТаблицаРезультата Цикл
СтруктураСтроки = Новый Структура("Номенклатура, Остаток", Строка.Номенклатура, Строка.Остаток);
МассивРезультата.Добавить(СтруктураСтроки);
КонецЦикла;
Возврат МассивРезультата;
КонецФункции
&НаКлиенте
Процедура ОбновитьТаблицуНаКлиенте(МассивДанных)
// Очищаем и перезаполняем коллекцию на клиенте
// Фокус ввода на текстовых полях вне этой таблицы при этом не пострадает
Объект.ОстаткиТоваров.Очистить();
Для Каждого Карго Из МассивДанных Цикл
НоваяСтрока = Объект.ОстаткиТоваров.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, Карго);
КонецЦикла;
КонецПроцедуры
Благодаря такому подходу мы полностью контролируем процесс обновления данных. Изменения затрагивают исключительно коллекцию остатков, а все остальные элементы интерфейса остаются в стабильном состоянии.
Если фоновое обновление мешает вводу данных в конкретных «чувствительных» полях, мы можем временно усыплять обработчик ожидания на время работы пользователя с этими полями.
Рассмотрим алгоритм реализации такой блокировки:
Enter (или перевел фокус на другой элемент), мы снова запускаем таймер.Посмотрим на пример кода, реализующий эту логику для числового поля ввода:
&НаКлиенте
Процедура ЧувствительноеПолеНачалоВыбора(Элемент, ДанныеВыбора, СтандартнаяОбработка)
// Пользователь нажал на кнопку выбора (например, открывает калькулятор)
// Временно отключаем обновление, чтобы калькулятор не закрылся
ОтключитьОбработчикОжидания("ОбработчикТиковСекунд");
КонецПроцедуры
&НаКлиенте
Процедура ЧувствительноеПолеПриИзменении(Элемент)
// Ввод успешно завершен, данные сохранены в реквизит.
// Снова включаем фоновое обновление формы
ПодключитьОбработчикОжидания("ОбработчикТиковСекунд", 5);
КонецПроцедуры
&НаКлиенте
Процедура ЧувствительноеПолеОкончаниеВводаТекста(Элемент, Текст, ДанныеВыбора, ПараметрыПолученияДанных, СтандартнаяОбработка)
// Дополнительная подстраховка на случай, если пользователь просто закончил ввод текста
ПодключитьОбработчикОжидания("ОбработчикТиковСекунд", 5);
КонецПроцедуры
Этот способ требует написания дополнительного кода для обработчиков событий элементов формы, но гарантирует абсолютный комфорт для пользователя при заполнении важных документов.
Постоянный опрос сервера каждые 5 секунд (пулинг) создает огромную паразитную нагрузку на СУБД и рабочие процессы 1С. Если форму откроют одновременно 50 пользователей, сервер будет непрерывно занят обработкой бессмысленных запросов, большая часть которых вернет те же самые данные.
Современное и наиболее технологичное решение — переход от постоянного опроса по таймеру к архитектуре получения уведомлений (push-уведомления) через встроенную в 1С Систему взаимодействия.
Принцип работы выглядит следующим образом:
Это полностью избавляет систему от холостых запросов каждые 5 секунд, снижает нагрузку на сервер до нуля в периоды отсутствия транзакций и гарантирует актуальность данных без ущерба для удобства работы пользователя.