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

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

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

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

Выясняем причину: как устроен жизненный цикл контекста управляемой формы

Давайте проанализируем ситуацию. В управляемом приложении форма физически разделена на две части: клиентскую (которая отображается пользователю на компьютере или в веб-браузере) и серверную (которая работает на сервере приложений 1С). Обмен данными между ними жестко регламентирован.

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

Рассмотрим по шагам, что происходит во время выполнения кода:

  1. Пользователь открывает форму. Начинается серверный вызов.
  2. На сервере создается контекст формы. Платформа выделяет память под вашу переменную модуля, объявленную с директивой &НаСервере.
  3. Срабатывает процедура ПриСозданииНаСервере. Вы присваиваете переменной значение, например, ссылку на документ или сложную структуру данных.
  4. Серверный вызов завершается, форма подготовлена к отображению и передается на клиент (компьютер пользователя).
  5. В этот момент серверный контекст формы полностью уничтожается! Переменная, созданная на сервере, прекращает свое существование в памяти. Все данные, которые в ней хранились, безвозвратно удаляются.
  6. Когда пользователь нажимает кнопку на форме или меняет значение поля, инициируется новый серверный вызов. Платформа вынуждена заново создавать серверный контекст формы. Соответственно, серверная переменная модуля инициализируется заново со значением по умолчанию — Неопределено.

А что если объявить переменную с директивой компиляции &НаКлиенте? В таком случае переменная действительно будет жить на клиенте все время, пока форма открыта. Однако к ней принципиально нельзя будет обратиться из серверных процедур — система выдаст ошибку компиляции. Таким образом, напрямую передать данные с сервера на клиент через классические переменные модуля управляемой формы невозможно.

Давайте разберем популярные, но неверные «обходные пути», которые часто пытаются использовать разработчики, и поймем, почему делать так не стоит.

Анализ нерекомендуемых решений

Использование ключевого слова Экспорт (словосочетание Перем МояПеременная Экспорт)

Экспортность лишь делает переменную модуля доступной для внешнего кода (например, при обращении к форме из других модулей), но она никак не влияет на клиент-серверный переход. Данные все так же будут уничтожаться при завершении каждого вызова сервера.

Использование системных хранилищ настроек (ХранилищеОбщихНастроек)

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

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

Решение 1. Использование реквизитов формы (самый надежный и простой способ)

Если вам необходимо сохранить между вызовами примитивные типы данных (число, строка, дата, булево) или ссылки на объекты базы данных (справочники, документы), лучшим решением будет использование реквизитов формы.

Реквизиты формы — это специальные объекты метаданных формы, которые платформа автоматически сериализует (упаковывает) и передает между клиентом и сервером при каждом контекстном вызове.

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

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

Теперь мы можем без проблем работать с этим реквизитом как в серверном, так и в клиентском контексте:


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    // Записываем значение в реквизит формы на сервере
    СохраненныйПараметр = "Значение, полученное при создании формы";
КонецПроцедуры

&НаКлиенте
Процедура При НажатииКнопки(Команда)
    // Без проблем читаем это значение на клиенте!
    ПоказатьПредупреждение(, СохраненныйПараметр);
КонецПроцедуры

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

Если значение определяется еще до момента открытия формы (например, в коде, который эту форму вызывает) и должно быть доступно как на сервере, так и на клиенте в процессе работы, правильнее использовать параметры формы.

Для того чтобы параметр не терялся при переходе между контекстами, его можно сделать ключевым. В окне редактирования параметров формы в конфигураторе установите для нужного параметра свойство «Ключевой». Значение такого параметра будет автоматически сохраняться в системной коллекции Параметры.

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


&НаКлиенте
Процедура ПроверитьПараметр()
    Если Параметры.Свойство("ИмяМоегоПараметра") Тогда
        ЗначениеПараметра = Параметры.ИмяМоегоПараметра;
        // Дальнейшая обработка полученного значения
    КонецЕсли;
КонецПроцедуры

Решение 3. Временное хранилище (для тяжелых данных)

Что делать, если вам нужно передать с сервера на клиент данные, которые нельзя или нецелесообразно хранить в реквизитах формы? Например, большую таблицу значений, массив сложных структур или двоичные данные. Гонять такие объемы информации туда-обратно при каждом клике пользователя через реквизиты формы крайне неэффективно — это приведет к зависанию интерфейса.

Для таких задач в 1С реализован механизм Временного хранилища. Данные помещаются в специальную область оперативной памяти сервера, а между клиентом и сервером передается лишь короткая строка-адрес (УРЛ), по которой эти данные можно прочитать.

Посмотрим на пример реализации этой схемы:

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

Рассмотрим код, реализующий данный алгоритм:


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

&НаКлиенте
Процедура ПолучитьДанныеНаКлиенте(Команда)
    
    // Вызываем серверную процедуру для безопасного извлечения данных по адресу
    ОбработатьДанныеИзХранилищаНаСервере();
    
КонецПроцедуры

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

Подведем итоги

Разработка под управляемые формы требует четкого понимания того, где в текущий момент выполняется ваш код — на клиенте или на сервере. Попытка использовать классические переменные модуля для передачи данных между контекстами обречена на неудачу из-за особенностей жизненного цикла серверного контекста управляемой формы.

Для надежной и быстрой работы приложения всегда следуйте простым правилам:

Использование этих штатных механизмов избавит ваш код от трудноуловимых ошибок с «внезапно исчезающими» значениями переменных и обеспечит стабильную работу системы в любых режимах работы платформы.

← На главную