При разработке в среде 1С:Предприятие 8 (особенно в управляемом приложении) начинающие программисты часто сталкиваются с тем, что переменные, объявленные в одной процедуре или функции, бесследно «исчезают» при переходе в другую. Ситуация усложняется, когда данные создаются на клиенте (например, при нажатии кнопки пользователем), а использоваться должны на сервере (например, для записи в базу данных или отправки почты).
В этой статье мы подробно разберем, почему возникает эта проблема, рассмотрим физику процесса передачи данных между клиентом и сервером, а также изучим пять надежных способов решения этой задачи на практических примерах.
Прежде чем перейти к коду, давайте проанализируем ситуацию. В управляемом приложении код четко разделен на контексты:
Когда вы объявляете переменную внутри клиентской процедуры (например, Профиль = Новый ИнтернетПочтовыйПрофиль), эта переменная живет ровно до тех пор, пока процедура не закончит свое выполнение. Как только выполнение доходит до ключевого слова КонецПроцедуры, оперативная память очищается, и переменная уничтожается. Попытка обратиться к ней из другой процедуры, вызванной вторым нажатием кнопки, закономерно вернет значение Неопределено.
Более того, сложный мутабельный объект (свойства которого могут изменяться динамически, например, почтовый профиль) невозможно напрямую передать с клиента на сервер, так как платформа не может его сериализовать (преобразовать в поток данных для передачи по сети).
Давайте рассмотрим эффективные способы решения этой задачи.
---
Это самый чистый и профессиональный подход с точки зрения архитектуры кода. Если одна процедура инициирует создание данных, а вторая должна их обработать в рамках одного пользовательского сценария, мы передаем данные напрямую через параметры.
Рассмотрим по шагам, как это реализовать:
&НаКлиенте
Процедура СоздатьИПодключитьПользователя(Команда)
// 1. Получаем данные (например, структуру с настройками)
ПараметрыПодключения = СформироватьПараметрыПодключения();
// 2. Передаем эти данные в качестве параметра в другую процедуру
ВыполнитьПодключение(ПараметрыПодключения);
КонецПроцедуры
&НаКлиенте
Функция СформироватьПараметрыПодключения()
Результат = Новый Структура;
Результат.Вставить("АдресСервераSMTP", "smtp.mail.ru");
Результат.Вставить("ПортSMTP", 465);
Результат.Вставить("ПользовательSMTP", "user@mail.ru");
Возврат Результат;
КонецФункции
&НаКлиенте
Процедура ВыполнитьПодключение(Знач Настройки)
// Здесь мы успешно используем переданную переменную "Настройки"
Сообщить("Подключаемся к серверу: " + Настройки.АдресСервераSMTP);
КонецПроцедуры
Обратите внимание на использование ключевого слова Знач в объявлении параметров процедуры ВыполнитьПодключение. Это позволяет передавать параметры по значению, предохраняя исходные данные от случайного изменения внутри подчиненной процедуры.
---
Что делать, если у вас на форме есть две независимые кнопки? Первая кнопка «Создать профиль» нажимается в один момент времени, а кнопка «Подключиться» — через некоторое время, когда пользователь проверил другие параметры.
В этом случае параметры нужно сохранить в контексте формы. Для этого идеально подходит реквизит формы.
Проанализируем алгоритм настройки:
АдресПодключения и типом Строка).Посмотрим на пример кода:
&НаКлиенте
Процедура КнопкаСоздатьПрофиль(Команда)
// Записываем данные в реквизит формы напрямую
Объект.СерверПодключения = "smtp.mail.ru";
Элементы.СостояниеПодключения.Заголовок = "Профиль подготовлен";
КонецПроцедуры
&НаКлиенте
Процедура КнопкаПодключиться(Команда)
// Проверяем, заполнен ли реквизит формы
Если ПустаяСтрока(Объект.СерверПодключения) Тогда
ВызватьИсключение "Сначала необходимо создать профиль подключения!";
КонецЕсли;
// Передаем значение реквизита формы в серверную процедуру
ВыполнитьПодключениеНаСервере(Объект.СерверПодключения);
КонецПроцедуры
&НаСервереБезКонтекста
Процедура ВыполнитьПодключениеНаСервере(Знач Сервер)
// Серверный код подключения...
// Здесь переменная "Сервер" успешно получена и готова к работе
КонецПроцедуры
Использование реквизитов формы гарантирует, что данные будут сохранены в течение всего сеанса работы пользователя с этой конкретной формой.
---
Часто разработчики пытаются сохранить объект типа ИнтернетПочтовыйПрофиль в реквизит формы с типом Произвольный. Это приводит к ошибке, так как платформа 1С не может передавать мутабельные типы данных между клиентом и сервером в рамках автоматического обновления контекста формы.
Решение этой проблемы — использование Структуры. Структура — это легковесный, неизменяемый при передаче (сериализуемый) тип данных, который без проблем путешествует через границу клиента и сервера.
Рассмотрим правильный паттерн проектирования такого взаимодействия:
&НаКлиенте
Процедура СоздатьПользователя(Команда)
// Собираем все настройки в обычную структуру на клиенте
НастройкиПочты = Новый Структура;
НастройкиПочты.Вставить("АдресСервераSMTP", "smtp.mail.ru");
НастройкиПочты.Вставить("ПортSMTP", 465);
НастройкиПочты.Вставить("ПользовательSMTP", "username");
НастройкиПочты.Вставить("ПарольSMTP", "secure_password");
НастройкиПочты.Вставить("ИспользоватьSSLSMTP", Истина");
// Вызываем серверную процедуру подключения и передаем структуру
ПодключитьНаСервере(НастройкиПочты);
КонецПроцедуры
&НаСервереБезКонтекста
Процедура ПодключитьНаСервере(Знач НастройкиПочты)
// На сервере инициализируем полноценный системный объект профиля
Профиль = Новый ИнтернетПочтовыйПрофиль;
// Заполняем свойства профиля из переданной структуры
ЗаполнитьЗначенияСвойств(Профиль, НастройкиПочты);
// Выполняем подключение к почте
Почта = Новый ИнтернетПочта;
Попытка
Почта.Подключиться(Профиль);
Сообщить("Подключение успешно выполнено на стороне сервера!");
Исключение
Сообщить("Ошибка подключения: " + ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
Использование функции ЗаполнитьЗначенияСвойств позволяет автоматически скопировать одноименные ключи из структуры в свойства объекта почтового профиля, что избавляет вас от необходимости писать рутинный код присвоения для каждого поля.
---
Если вам все же необходимо передать сложный объект или большую таблицу значений, которую не хочется гонять туда-сюда через контекст формы (что сильно замедляет работу интерфейса), лучшим выбором будет использование Временного хранилища.
Разберем этот механизм по шагам:
ПоместитьВоВременноеХранилище().УникальныйИдентификатор нашей управляемой формы (это гарантирует очистку памяти при закрытии формы).ПолучитьИзВременногоХранилища().Посмотрим на пример программной реализации:
&НаКлиенте
Процедура СохранитьДанныеВоВременноеХранилище(Команда)
ДанныеДляПередачи = Новый Структура("Ключ", "Очень важное значение");
// Помещаем данные в хранилище и получаем строковый адрес
АдресХранилища = ПоместитьВоВременноеХранилище(ДанныеДляПередачи, ЭтаФорма.УникальныйИдентификатор);
// Сохраняем только адрес (строку) в реквизит формы
Объект.АдресВременныхДанных = АдресХранилища;
КонецПроцедуры
&НаКлиенте
Процедура ПрочитатьИзВременногоХранилища(Команда)
Если ПустаяСтрока(Объект.АдресВременныхДанных) Тогда
Возврат;
КонецЕсли;
// Извлекаем данные на сервере по сохраненному адресу
ОбработатьДанныеНаСервере(Объект.АдресВременныхДанных);
КонецПроцедуры
&НаСервереБезКонтекста
Процедура ОбработатьДанныеНаСервере(Знач Адрес)
// Получаем исходную структуру из хранилища
Данные = ПолучитьИзВременногоХранилища(Адрес);
Если Данные <> Неопределено Тогда
Сообщить("Успешно прочитано значение: " + Данные.Ключ);
КонецЕсли;
КонецПроцедуры
---
Если вся работа с переменными происходит исключительно на стороне клиента (без вызовов сервера) и данные нужны только для связи нескольких интерактивных событий, вы можете объявить переменную модуля формы с директивой &НаКлиенте.
Важно помнить: такие переменные сохраняют свои значения до тех пор, пока открыто окно формы, но они абсолютно недоступны на сервере.
&НаКлиенте
Перем КлиентскийПрофиль; // Объявление переменной в самом начале модуля
&НаКлиенте
Процедура КнопкаЗаполнить(Команда)
// Инициализируем переменную модуля
КлиентскийПрофиль = "Параметры_Клиента";
КонецПроцедуры
&НаКлиенте
Процедура КнопкаПроверить(Команда)
// Переменная сохраняет значение между нажатиями разных кнопок
Если КлиентскийПрофиль <> Неопределено Тогда
ПоказатьПредупреждение(, "Данные сохранены на клиенте: " + КлиентскийПрофиль);
КонецЕсли;
КонецПроцедуры
Используйте этот метод с осторожностью. Если ваша форма осуществляет частые серверные вызовы без передачи контекста или пересоздает форму, данные в переменной модуля могут быть утеряны или сброшены. В промышленной разработке более надежным и контролируемым подходом считается явное использование реквизитов формы или параметров процедур.