В разработке на управляемых формах 1С часто встречается задача: при обновлении динамического списка нужно синхронно обновить и другие элементы на форме. Например, вывести актуальное количество записей в списке. Сложность заключается в том, что основной серверный обработчик динамического списка, ПриПолученииДанныхНаСервере, выполняется без контекста формы. Это означает, что из него нельзя напрямую обратиться к реквизитам или элементам формы и изменить их значения.
Давайте разберем, как обойти это ограничение и добиться синхронного обновления данных.
Рассмотрим типичный сценарий. У нас есть форма с динамическим списком, для которого настроено автоматическое обновление каждые 60 секунд. Мы хотим, чтобы рядом со списком отображалась надпись "Всего записей: N", где N — это общее количество записей, соответствующее текущим отборам списка.
Основное событие, которое срабатывает при обновлении списка, — это ПриПолученииДанныхНаСервере. Оно имеет директиву компиляции &НаСервереБезКонтекста. Это сделано платформой для оптимизации: серверу не нужно получать и хранить все данные формы для простого формирования порции данных списка. Но из-за этого мы не можем написать простой код вроде:
&НаСервереБезКонтекста
Процедура СписокПриПолученииДанныхНаСервере(ИмяЭлемента, Настройки, Строки)
// Этот код вызовет ошибку, так как РеквизитФормы не существует в данном контексте
// РеквизитФормы = РассчитатьКоличество();
КонецПроцедуры
Наша задача — найти способ "пробросить" нужные данные из серверного вызова обратно на клиентскую форму.
Самый быстрый способ — это выполнять расчет количества записей параллельно с обновлением списка, но в отдельном потоке. Для этого идеально подходит механизм обработчиков ожидания.
Суть метода: мы запускаем на клиенте таймер, который с заданной периодичностью будет вызывать серверную процедуру для расчета количества записей и обновления соответствующего реквизита на форме. Этот таймер будет работать независимо от таймера обновления динамического списка.
Рассмотрим по шагам:
КоличествоЗаписей (тип Число или Строка), и выведем его на форму как поле вида "Надпись".
// Клиентская процедура, которую будет вызывать обработчик ожидания
&НаКлиенте
Процедура ОбновитьСчетчикЗаписей()
ОбновитьСчетчикЗаписейНаСервере();
КонецПроцедуры
// Серверная процедура, выполняющая расчет
&НаСервере
Процедура ОбновитьСчетчикЗаписейНаСервере()
// Здесь ваш запрос, который быстро считает количество.
// Важно: он должен учитывать те же отборы, что и динамический список.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| КОЛИЧЕСТВО(Заказы.Ссылка)
|ИЗ
| Документ.ЗаказКлиента КАК Заказы";
Результат = Запрос.Выполнить().Выгрузить();
КоличествоЗаписей = Результат[0][0];
КонецПроцедуры
ПриОткрытии запустим наш таймер.
&НаКлиенте
Процедура ПриОткрытии(Отказ)
// Обновлять счетчик каждые 60 секунд.
ПодключитьОбработчикОжидания("ОбновитьСчетчикЗаписей", 60);
КонецПроцедуры
&НаКлиенте
Процедура ПриЗакрытии()
ОтключитьОбработчикОжидания("ОбновитьСчетчикЗаписей");
КонецПроцедуры
Преимущества:
Главный недостаток:
Этот метод позволяет добиться полной синхронизации. Идея заключается в том, чтобы передать данные между клиентом и сервером "окольным путем" — через временное хранилище. Мы не сможем передать данные *из* ПриПолученииДанныхНаСервере, но мы можем выполнить расчет *перед* обновлением списка, сохранить результат, а затем, *после* обновления, забрать его.
Этот подход идеально подходит для сценариев, где обновление инициируется пользователем (например, по кнопке "Обновить"), но его можно адаптировать и для автоматического обновления.
Разберем по шагам на примере ручного обновления по кнопке.
ОбновитьДанные.
&НаКлиенте
Процедура ОбновитьДанные(Команда)
// Шаг 1: Вызываем сервер, чтобы он рассчитал количество и сохранил в хранилище.
// Сервер вернет нам адрес, по которому лежат данные.
АдресРезультата = "";
АдресРезультата = ПодготовитьДанныеДляОбновленияНаСервере();
// Шаг 2: Обновляем сам динамический список.
Элементы.Список.Обновить();
// Шаг 3: После обновления списка, забираем результат с сервера по известному адресу.
ПолучитьРезультатОбновленияНаКлиенте(АдресРезультата);
КонецПроцедуры
// Процедура для шага 1
&НаСервере
Функция ПодготовитьДанныеДляОбновленияНаСервере()
// Выполняем быстрый запрос для подсчета количества
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| КОЛИЧЕСТВО(Заказы.Ссылка)
|ИЗ
| Документ.ЗаказКлиента КАК Заказы";
РезультатЗапроса = Запрос.Выполнить().Выгрузить()[0][0];
// Помещаем результат во временное хранилище и возвращаем его адрес клиенту
Адрес = ПоместитьВоВременноеХранилище(РезультатЗапроса, УникальныйИдентификатор);
Возврат Адрес;
КонецФункции
// Процедура для шага 3
&НаКлиенте
Процедура ПолучитьРезультатОбновленияНаКлиенте(АдресРезультата)
Если НЕ ЗначениеЗаполнено(АдресРезультата) Тогда
Возврат;
КонецЕсли;
// Вызываем сервер, чтобы он достал данные из хранилища
ПолучитьРезультатОбновленияНаСервере(АдресРезультата);
КонецПроцедуры
&НаСервере
Процедура ПолучитьРезультатОбновленияНаСервере(АдресРезультата)
// Получаем данные по адресу
Данные = ПолучитьИзВременногоХранилища(АдресРезультата);
// Если данные существуют, обновляем реквизит формы
Если Данные <> Неопределено Тогда
КоличествоЗаписей = Данные;
КонецЕсли;
// Временное хранилище больше не нужно, его можно удалить.
// Однако, платформа сама очищает его через некоторое время.
// УдалитьИзВременногоХранилища(АдресРезультата);
КонецПроцедуры
Как это работает? Мы выстраиваем четкую последовательность действий:
В итоге, обновление счетчика происходит сразу после обновления списка, что гарантирует их синхронность. Этот метод сложнее, но он обеспечивает полный контроль над процессом и является самым надежным решением данной задачи.
Проблема обновления контекстных данных формы из безконтекстного серверного вызова — классическая задача в разработке на управляемых формах 1С. Выбор решения зависит от требований к точности и синхронизации данных.
ОбработчикомОжидания.ВременнымХранилищем и управляемой последовательностью вызовов "клиент-сервер-клиент".Понимая принципы работы этих механизмов, вы сможете эффективно управлять поведением форм и предоставлять пользователю актуальную информацию в профессиональном интерфейсе.