Почему сбрасываются значения переменных модуля управляемой формы после выполнения процедуры на сервере?

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

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

В чем причина сброса переменных модуля формы

Проанализируем архитектуру клиент-серверного взаимодействия в управляемых формах. Модуль управляемой формы компилируется на клиенте и на сервере раздельно, в зависимости от директив компиляции. Когда мы объявляем переменные в начале модуля без явного указания директивы:


Перем ДокМенееНормы Экспорт;
Перем ДокВышеНормы Экспорт;

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

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

Хранить значения в серверных переменных модуля формы можно исключительно в рамках одного и того же непрерывного серверного вызова — если одна процедура сервера вызывает другую процедуру того же модуля.

Способ 1. Использование реквизитов формы

Если данные должны сохранять свое значение на протяжении всей жизни открытой формы и быть одинаково доступны как в процедурах с директивой &НаКлиенте, так и с директивой &НаСервере, правильным решением является использование реквизитов формы.

Реквизиты формы, в отличие от переменных модуля, сериализуются платформой и автоматически передаются в составе контекста между клиентом и сервером при контекстных вызовах.

Разберем по шагам, как настроить этот механизм:

  1. Откроем редактор формы в конфигураторе и перейдем на вкладку Реквизиты.
  2. Добавим новые реквизиты: ДокВышеНормы и ДокМенееНормы с соответствующим типом (например, Число).
  3. Удалим объявление переменных Перем из текста модуля.
  4. В серверной процедуре запишем значения непосредственно в созданные реквизиты:

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

Если реквизиты не были созданы заранее в конфигураторе, их можно создать программно на сервере (например, в обработчике ПриСозданииНаСервере) с помощью метода ИзменитьРеквизиты(), сформировав массив объектов РеквизитФормы.

Способ 2. Клиентские переменные модуля и вызовы без контекста

Переменные модуля формы могут существовать на протяжении всего времени работы формы, но только если они скомпилированы на клиенте. Для этого перед ними обязательно указывается директива &НаКлиенте.

В этом случае оптимальным подходом с точки зрения производительности является вызов функции с директивой &НаСервереБезКонтекста. Такая функция получает данные из базы и возвращает структуру, не перегоняя по сети лишние данные всей формы. Посмотрим на пример реализации:


&НаКлиенте
Перем ДокМенееНормы;
&НаКлиенте
Перем ДокВышеНормы;

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

&НаКлиенте
Процедура ЗаполнитьОценкиНаКлиенте()
    Оценки = ПолучитьЗначенияОценок();
    ДокВышеНормы = Оценки.ВышеНормы;
    ДокМенееНормы = Оценки.НижеНормы;
КонецПроцедуры

Здесь переменные ДокМенееНормы и ДокВышеНормы будут успешно сохранять свои значения между различными событиями на стороне клиента до момента закрытия окна формы.

Оптимизация выборки и работа с объемными данными

Обратим внимание на частые сопутствующие проблемы, возникающие при реализации подобных задач:

  1. Фильтрация данных в теле запроса. В исходном примере выборка производилась по всей табличной части справочника без условий, а отбор происходил в цикле на языке 1С. Всегда переносите фильтры в блок ГДЕ запроса — это существенно экономит память сервера и снижает нагрузку на СУБД.
  2. Ограничения типов для реквизитов формы. Реквизиты формы предназначены только для типов, поддерживающих сериализацию. Если требуется временно сохранить сложный или тяжелый объект (например, двоичные данные или массив структур), используйте метод ПоместитьВоВременноеХранилище() с привязкой к УникальныйИдентификатор формы, а в реквизитах или переменных храните только полученную строку-адрес.
  3. Кэширование постоянных настроек. Если шкалы премий и нормативы меняются редко, логично вынести их чтение в общий модуль с включенным свойством повторного использования возвращаемых значений. Это избавит базу данных от дублирующих запросов при каждом пересчете.
← На главную