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