Как обновить данные на форме при обновлении динамического списка в 1С?

Программист 1С v8.3 (Управляемые формы)
← На главную

В разработке на управляемых формах 1С часто встречается задача: при обновлении динамического списка нужно синхронно обновить и другие элементы на форме. Например, вывести актуальное количество записей в списке. Сложность заключается в том, что основной серверный обработчик динамического списка, ПриПолученииДанныхНаСервере, выполняется без контекста формы. Это означает, что из него нельзя напрямую обратиться к реквизитам или элементам формы и изменить их значения.

Давайте разберем, как обойти это ограничение и добиться синхронного обновления данных.

Анализ проблемы: почему стандартный подход не работает?

Рассмотрим типичный сценарий. У нас есть форма с динамическим списком, для которого настроено автоматическое обновление каждые 60 секунд. Мы хотим, чтобы рядом со списком отображалась надпись "Всего записей: N", где N — это общее количество записей, соответствующее текущим отборам списка.

Основное событие, которое срабатывает при обновлении списка, — это ПриПолученииДанныхНаСервере. Оно имеет директиву компиляции &НаСервереБезКонтекста. Это сделано платформой для оптимизации: серверу не нужно получать и хранить все данные формы для простого формирования порции данных списка. Но из-за этого мы не можем написать простой код вроде:


&НаСервереБезКонтекста
Процедура СписокПриПолученииДанныхНаСервере(ИмяЭлемента, Настройки, Строки)
	
	// Этот код вызовет ошибку, так как РеквизитФормы не существует в данном контексте
	// РеквизитФормы = РассчитатьКоличество(); 
	
КонецПроцедуры

Наша задача — найти способ "пробросить" нужные данные из серверного вызова обратно на клиентскую форму.

Решение 1: Простой, но неточный метод через обработчик ожидания

Самый быстрый способ — это выполнять расчет количества записей параллельно с обновлением списка, но в отдельном потоке. Для этого идеально подходит механизм обработчиков ожидания.

Суть метода: мы запускаем на клиенте таймер, который с заданной периодичностью будет вызывать серверную процедуру для расчета количества записей и обновления соответствующего реквизита на форме. Этот таймер будет работать независимо от таймера обновления динамического списка.

Рассмотрим по шагам:

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

Преимущества:

Главный недостаток:

Решение 2: Комплексный и надежный метод с Временным хранилищем

Этот метод позволяет добиться полной синхронизации. Идея заключается в том, чтобы передать данные между клиентом и сервером "окольным путем" — через временное хранилище. Мы не сможем передать данные *из* ПриПолученииДанныхНаСервере, но мы можем выполнить расчет *перед* обновлением списка, сохранить результат, а затем, *после* обновления, забрать его.

Этот подход идеально подходит для сценариев, где обновление инициируется пользователем (например, по кнопке "Обновить"), но его можно адаптировать и для автоматического обновления.

Разберем по шагам на примере ручного обновления по кнопке.

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

Как это работает? Мы выстраиваем четкую последовательность действий:

  1. Клиент просит сервер посчитать данные и "отложить" их в сторону (во временное хранилище).
  2. Сервер выполняет просьбу и возвращает "квиток" с адресом, где лежат данные.
  3. Клиент, получив "квиток", запускает штатное обновление динамического списка.
  4. Сразу после этого клиент снова обращается к серверу, предъявляет "квиток" и просит отдать данные.
  5. Сервер отдает данные, клиент обновляет надпись на форме.

В итоге, обновление счетчика происходит сразу после обновления списка, что гарантирует их синхронность. Этот метод сложнее, но он обеспечивает полный контроль над процессом и является самым надежным решением данной задачи.

Выводы

Проблема обновления контекстных данных формы из безконтекстного серверного вызова — классическая задача в разработке на управляемых формах 1С. Выбор решения зависит от требований к точности и синхронизации данных.

Понимая принципы работы этих механизмов, вы сможете эффективно управлять поведением форм и предоставлять пользователю актуальную информацию в профессиональном интерфейсе.

← На главную