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