Как обновить таблицу значений на управляемой форме и сохранить параметры запроса?

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

При переходе с обычных форм на управляемые разработчики часто сталкиваются с изменением парадигмы работы с данными. В обычных формах мы могли объявить глобальную переменную Запрос на уровне модуля формы, настроить её один раз при открытии, а затем использовать при нажатии кнопки «Обновить». В управляемых формах контекст клиента и сервера разделен, и хранить несериализуемые серверные объекты (такие как Запрос) между вызовами сервера невозможно. Параметры формы, переданные при открытии, также уничтожаются после выполнения обработчика ПриСозданииНаСервере.

В этой статье мы подробно разберем, как правильно организовать архитектуру обновления ТаблицыЗначений на клиенте, исправим распространенные ошибки разработчиков и рассмотрим три надежных метода реализации этой задачи в соответствии со стандартами разработки фирмы «1С».

Критическая ошибка: попытка сериализации объекта «Запрос»

В процессе обсуждения часто возникает идея поместить сам объект Запрос во временное хранилище, чтобы при обновлении просто достать его оттуда. Например, с помощью такого кода:


// Код содержит критическую ошибку!
АдресВХранилище = ПоместитьВоВременноеХранилище(Новый Структура("Запрос", Запрос), УникальныйИдентификатор);

Внимание: этот код работать не будет! Объект Запрос в платформе «1С:Предприятие 8» является исключительно серверным и не поддерживает сериализацию (у него отсутствует признак «Сериализуется» в синтакс-помощнике). При попытке выполнить эту операцию платформа сгенерирует ошибку: «Переданное значение не может быть помещено во временное хранилище». Во временное хранилище можно помещать только те типы данных, которые могут быть сохранены на диске или переданы по сети.

Также категорически не рекомендуется использовать устаревшие функции ЗначениеВСтрокуВнутр и ЗначениеИзСтрокиВнутр. Формат этих функций не документирован, предназначен только для обратной совместимости с версией 7.7 и может быть изменен или полностью удален в будущих версиях платформы.

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

---

Решение №1. Использование Временного Хранилища для хранения параметров и текста

Поскольку сам объект Запрос сохранить нельзя, мы можем сохранить его составляющие, которые отлично сериализуются: текст запроса (тип Строка) и его параметры (тип Структура, где ключами выступают имена параметров, а значениями — примитивные типы или ссылки базы данных).

Проанализируем пошаговый алгоритм реализации этого подхода:

  1. При создании формы на сервере: собираем динамический текст запроса, заполняем структуру параметров, упаковываем их в общую структуру и помещаем во временное хранилище с привязкой к уникальному идентификатору формы.
  2. При обновлении данных: извлекаем структуру из хранилища по сохраненному адресу, создаем новый объект Запрос, обходим параметры в цикле и устанавливаем их, после чего выполняем запрос и выгружаем результат в таблицу на форме.

Рассмотрим пример реализации на встроенном языке:


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

&НаСервере
Процедура ОбновитьТаблицуЗначенийНаСервере()
    
    // Получаем структуру данных из хранилища
    ДанныеЗапроса = ПолучитьИзВременногоХранилища(АдресЗапросаВоХранилище);
    
    Если ДанныеЗапроса = Неопределено Тогда
        ВызватьИсключение "Ошибка получения параметров запроса. Попробуйте переоткрыть форму.";
    КонецЕсли;
    
    // Создаем новый объект запроса
    Запрос = Новый Запрос(ДанныеЗапроса.Текст);
    
    // Заполняем параметры в цикле
    Для Каждого ТекущийПараметр Из ДанныеЗапроса.Параметры Цикл
        Запрос.УстановитьПараметр(ТекущийПараметр.Ключ, ТекущийПараметр.Значение);
    КонецЦикла;
    
    // Выполняем запрос и выгружаем результат в реквизит формы (ТаблицуЗначений)
    РезультатЗапроса = Запрос.Выполнить().Выгрузить();
    ЗначениеВРеквизитФормы(РезультатЗапроса, "ТаблицаДанных");
    
КонецПроцедуры

&НаКлиенте
Процедура ОбновитьТЗ(Команда)
    ОбновитьТаблицуЗначенийНаСервере();
КонецПроцедуры

---

Решение №2. Использование реквизитов формы в качестве параметров

Если состав параметров запроса заранее известен и статичен (например, мы всегда фильтруем данные по периоду, складу и организации), самым простым, надежным и декларативным решением будет создание соответствующих реквизитов формы (например, ПериодНачала, ПериодОкончания, ВыбранныйСклад).

Выясним преимущества этого метода:

Посмотрим на пример кода серверной процедуры обновления при использовании реквизитов формы:


&НаСервере
Процедура ОбновитьТаблицуПоРеквизитамНаСервере()
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |	Остатки.Номенклатура КАК Номенклатура,
        |	Остатки.ВНаличииОстаток КАК Остаток
        |ИЗ
        |	РегистрНакопления.ТоварыНаСкладах.Остатки(&Период, Склад = &Склад) КАК Остатки";
    
    // Напрямую обращаемся к реквизитам формы
    Запрос.УстановитьПараметр("Период", ПериодОстатков); // Реквизит формы (Дата)
    Запрос.УстановитьПараметр("Склад", ВыбранныйСклад);   // Реквизит формы (СправочникСсылка.Склады)
    
    Результат = Запрос.Выполнить().Выгрузить();
    ЗначениеВРеквизитФормы(Результат, "ТаблицаОстатков");
    
КонецПроцедуры

---

Решение №3. Использование Компоновщика настроек (СКД)

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

Объект КомпоновщикНастроекКомпоновкиДанных является сериализуемым и может быть добавлен непосредственно в качестве реквизита управляемой формы.

Разберем по шагам, как это работает:

  1. В схеме компоновки данных (СКД) описывается запрос со всеми возможными параметрами и отборами.
  2. В реквизитах формы создается реквизит с типом КомпоновщикНастроекКомпоновкиДанных.
  3. При создании формы на сервере компоновщик инициализируется схемой компоновки. Его настройки выводятся на форму для управления пользователем.
  4. При нажатии кнопки «Обновить» программно запускается процессор компоновки данных, который на основе текущих настроек компоновщика формирует и выполняет итоговый запрос, выводя результат в таблицу формы.

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

---

Важный нюанс: работа с Временными Таблицами (ВТ)

Если в вашем запросе используются временные таблицы (например, вы передаете в запрос внешнюю таблицу значений, помещаете её в менеджер временных таблиц и затем соединяете с другими таблицами БД), важно учитывать следующее правило:

Менеджер временных таблиц не сохраняется между вызовами сервера.

При нажатии кнопки «Обновить» на клиенте управление переходит на сервер, но старый контекст временных таблиц уже уничтожен. Поэтому при обновлении данных вам придется заново передавать исходную таблицу значений на сервер, инициализировать МенеджерВременныхТаблиц, заново наполнять его и только после этого выполнять основной запрос.

← На главную