При разработке конфигураций в 1С часто возникает задача передать данные из одной формы в другую, особенно когда одна из форм закрывается после взаимодействия с пользователем. Классический подход с модальными окнами, когда вызывающая форма "замирает" в ожидании результата, становится недоступным в современных условиях, таких как работа в веб-клиенте, на мобильных устройствах или в режиме сервиса. Именно поэтому в 1С предусмотрен запрет модального отображения окон, требующий использования асинхронных механизмов. В этой статье мы подробно рассмотрим, как реализовать передачу структуры с клиента на сервер после успешного закрытия немодального диалога.
Прежде чем погрузиться в решения, давайте выясним, почему мы сталкиваемся с такой задачей. Запрет модального отображения окон — это рекомендуемая практика разработки для конфигураций, ориентированных на современную архитектуру 1С:Предприятие 8.3. Модальные окна, привычные для обычных форм и толстого клиента, могут вызывать ряд проблем, особенно в веб-клиенте или на мобильных устройствах:
Таким образом, необходимость передачи данных из немодальных окон требует использования асинхронных механизмов, которые позволяют продолжить выполнение кода в вызывающей форме после закрытия дочерней формы или диалога.
Основным и наиболее часто используемым инструментом для организации асинхронного взаимодействия и передачи данных при закрытии немодальных форм является объект ОписаниеОповещения. Этот механизм позволяет указать процедуру, которая будет вызвана после закрытия немодального окна или завершения асинхронной операции (например, при ответе пользователя на вопрос, вводе значения или закрытии формы, открытой с определенными параметрами).
Механизм ОписаниеОповещения создается на стороне клиента и содержит информацию о процедуре-обработчике, которая будет вызвана в будущем. Мы указываем этой процедуре имя экспортируемой процедуры и, при необходимости, контекст или дополнительные параметры. Когда событие, на которое мы подписывались, происходит (например, пользователь нажал "Да" в окне вопроса), платформа вызывает указанную нами процедуру, передавая ей результат события и наши дополнительные параметры.
ОписаниеОповещения, указывая имя клиентской процедуры, которая должна быть вызвана, и объект-источник (чаще всего ЭтаФорма), в контексте которого эта процедура будет найдена. Мы также можем передать дополнительные параметры, которые будут доступны в процедуре-обработчике.ПоказатьВопрос(), ПоказатьПредупреждение(), ПоказатьЗначение() или ОткрытьФорму()), передавая ей созданное ОписаниеОповещения.ОписаниеОповещения. В этой процедуре мы получаем результат взаимодействия (например, нажатую кнопку) и можем выполнить дальнейшую логику, в том числе передать данные на сервер.Рассмотрим пример, когда мы хотим задать пользователю вопрос и, в зависимости от его ответа, передать определенную структуру на сервер.
&НаКлиенте
Процедура ЗадатьВопросПользователю()
// Создаем структуру, которую, возможно, захотим передать на сервер.
// Она будет доступна в обработчике оповещения.
ДополнительныеПараметры = Новый Структура;
ДополнительныеПараметры.Вставить("ДанныеДляПередачи", "НекоторыеДанныеИзКлиента");
ДополнительныеПараметры.Вставить("ИдентификаторОперации", Новый УникальныйИдентификатор());
// Создаем описание оповещения.
// "ОбработкаОтветаНаВопрос" - это имя клиентской процедуры,
// которая будет вызвана после ответа пользователя.
// "ЭтаФорма" - контекст, в котором будет найдена процедура.
// ДополнительныеПараметры - передаваемые в обработчик параметры.
Описание = Новый ОписаниеОповещения("ОбработкаОтветаНаВопрос", ЭтаФорма, ДополнительныеПараметры);
// Задаем вопрос.
// Если пользователь нажмет "Да", то в ОбработкаОтветаНаВопрос
// ПараметрРезультатаБудет Кнопка.Да.
ПоказатьВопрос(Описание, "Вы уверены, что хотите выполнить операцию?", РежимДиалогаВопрос.ДаНет, , "Важный вопрос");
КонецПроцедуры
&НаКлиенте
Процедура ОбработкаОтветаНаВопрос(РезультатВопроса, ДополнительныеПараметры) Экспорт
// Эта процедура будет вызвана после того, как пользователь ответит на вопрос.
// РезультатВопроса - это нажатая кнопка (например, Кнопка.Да или Кнопка.Нет).
// ДополнительныеПараметры - это структура, которую мы передали при создании ОписаниеОповещения.
Если РезультатВопроса = КодВозвратаДиалога.Да Тогда
Сообщить("Пользователь нажал 'Да'.");
// Создаем структуру для передачи на сервер
СтруктураДляСервера = Новый Структура;
СтруктураДляСервера.Вставить("РезультатДействия", "Успешно");
СтруктураДляСервера.Вставить("КлиентскиеДанные", ДополнительныеПараметры.ДанныеДляПередачи);
СтруктураДляСервера.Вставить("ИД_Операции", ДополнительныеПараметры.ИдентификаторОперации);
// ... добавляем другие нужные данные
// Вызываем серверную процедуру, передавая ей структуру
ПередатьСтруктуруНаСервер(СтруктураДляСервера);
ИначеЕсли РезультатВопроса = КодВозвратаДиалога.Нет Тогда
Сообщить("Пользователь нажал 'Нет'. Операция отменена.");
КонецЕсли;
КонецПроцедуры
&НаСервере
Процедура ПередатьСтруктуруНаСервер(ПолученнаяСтруктура)
// Здесь мы работаем с полученной структурой на сервере
Сообщить("На сервере получена структура. Результат действия: " + ПолученнаяСтруктура.РезультатДействия);
Сообщить("Клиентские данные: " + ПолученнаяСтруктура.КлиентскиеДанные);
Сообщить("ИД операции: " + ПолученнаяСтруктура.ИД_Операции);
// ... выполняем серверную логику, запись в базу данных и т.д.
КонецПроцедуры
В этом примере мы видим полный цикл: от инициирования вопроса на клиенте до обработки ответа и передачи данных на сервер. Ключевым моментом является то, что процедура ПередатьСтруктуруНаСервер() вызывается уже после того, как пользователь завершил взаимодействие с диалогом, что соответствует нашему требованию.
Еще одним мощным и гибким механизмом для асинхронной передачи данных между формами, особенно для двустороннего обмена или для оповещения нескольких форм, является пара методов Оповестить() и ОбработкаОповещения(). Этот подход позволяет одной форме отправить "сообщение" другим формам, которые "подписаны" на определенные события.
Оповестить(ИмяСобытия, Параметр, Источник).
ИмяСобытия: Строка, которая служит ключом для идентификации события. Формы-получатели будут реагировать на этот ключ.Параметр: Произвольные данные (например, Структура, Массив, ТаблицаЗначений), которые мы хотим передать. Именно здесь будет наша структура.Источник: Ссылка на форму, которая инициировала оповещение (обычно ЭтаФорма).ОбработкаОповещения(ИмяСобытия, Параметр, Источник). Когда вызывается Оповестить(), платформа инициирует эту процедуру во всех открытых формах, где она присутствует. Внутри этой процедуры мы анализируем ИмяСобытия, чтобы понять, какое событие произошло, и извлекаем данные из Параметр.Рассмотрим пример, представленный в сообщении 6 форума, который демонстрирует, как настроить двустороннюю связь и передать данные. Здесь у нас есть основная форма и форма настроек, открываемая немодально. Форма настроек передает данные обратно в основную форму при закрытии или определенном действии.
Шаг 1: Основная форма (Вызывающая форма)
В основной форме мы открываем форму настроек, указывая текущую форму как её владельца. Это позволяет форме настроек легко обратиться к своему владельцу.
// для основной формы. Нужно указать владельца формы для этой и открываемой форм
&НаКлиенте
Процедура ОткрытьНастройки(Команда)
// Открываем форму настроек, передавая текущую форму как владельца.
// Это ключевой момент для обратной связи.
ФормаНастроек = ПолучитьФорму("ВнешняяОбработка.ДвеФормы.Форма.ФормаНастройки",,ЭтаФорма);
// ВладелецФормы = ФормаНастроек; // Это скорее всего ошибка в оригинальном примере.
// Обычно, если мы хотим, чтобы открываемая форма знала владельца,
// то владелец указывается в ПолучитьФорму().
// Если же основная форма должна знать открытую как "своего владельца",
// то это менее типично. Будем считать, что ЭтаФорма является владельцем ФормыНастроек.
// В контексте сообщения 6, скорее всего, имелось в виду, что "ВладелецФормы"
// в ОткрытьНастройки присваивается, если текущая форма сама является открытой и ей нужен "владелец".
// Для данной задачи достаточно, что ФормаНастроек получает "ЭтаФорма" как ВладелецФормы.
ФормаНастроек.Открыть();
КонецПроцедуры
Далее, в этой же основной форме нам нужно реализовать процедуру ОбработкаОповещения, чтобы получать данные от формы настроек.
&НаКлиенте
Процедура ОбработкаОповещения(ИмяСобытия, Параметр, Источник)
// В этой процедуре мы получаем оповещения от других форм.
// "ИмяСобытия" позволяет нам отфильтровать нужные нам оповещения.
Если ИмяСобытия = "ДиалогФорм" Тогда
// Проверяем, что полученный Параметр является структурой и содержит нужные данные.
Если ТипЗнч(Параметр) = Тип("Структура") И Параметр.Свойство("Дата") Тогда
// Обновляем данные в текущей форме на основе полученных.
Объект.Дата = Параметр.Дата;
Сообщить("Дата успешно обновлена из формы настроек: " + Объект.Дата);
// Если нам нужно передать эту дату на сервер, делаем это здесь:
// ПередатьДатуНаСервер(Объект.Дата); // Вызов серверной процедуры
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Шаг 2: Форма настроек (Открываемая форма)
В форме настроек, когда пользователь совершил действие (например, нажал кнопку "Сохранить" или "Да"), мы используем метод Оповестить() для передачи данных обратно в форму-владельца.
// процедуры нужно привязать к событиям, действиям...
&НаКлиенте
Процедура ОповеститьВладельцаФормы(Команда)
// Проверяем, что у формы есть владелец, которому можно отправить оповещение.
Если ВладелецФормы = Неопределено Тогда
Сообщить("Не найдена форма - получатель оповещения (ВладелецФормы не установлен).");
Возврат;
КонецЕсли;
// Создаем структуру с данными, которые хотим передать.
Данные = Новый Структура;
Данные.Вставить("Дата", Объект.Дата); // Пример: передаем значение реквизита "Дата" из этой формы
// Определяем имя события, по которому владелец будет реагировать.
ИмяСобытия = "ДиалогФорм";
// Указываем Параметр и Источник оповещения.
Параметр = Данные;
Источник = ЭтаФорма; // Текущая форма является источником оповещения
// Отправляем оповещение. Все формы, у которых определена
// ОбработкаОповещения и которые "слушают" это ИмяСобытия,
// получат это оповещение.
Оповестить(ИмяСобытия, Параметр, Источник);
// После отправки оповещения, мы можем закрыть текущую форму, если это необходимо.
// Закрыть();
КонецПроцедуры
Этот пример наглядно демонстрирует, как организовать обратную связь и передачу структуры между немодальными формами. После того как основная форма получит оповещение и обновит свои данные, мы можем вызвать серверную процедуру для сохранения этих данных.
Независимо от того, какой механизм вы использовали для получения структуры на клиенте (ОписаниеОповещения или ОбработкаОповещения()), финальный шаг передачи данных на сервер остается одинаковым. После того как в клиентской процедуре у вас есть необходимая структура, вы просто вызываете серверную процедуру, передавая эту структуру в качестве параметра.
Предположим, у нас есть процедура на клиенте, которая уже получила или сформировала структуру СтруктураСКлиента. Мы хотим передать её для обработки на сервер.
// Пример на клиенте после получения данных из закрытого диалога или из ОбработкаОповещения
&НаКлиенте
Процедура ОбработкаУспешногоЗавершенияДиалога(Параметр)
Если ТипЗнч(Параметр) = Тип("Структура") Тогда
// Параметр содержит структуру с клиента
СтруктураДляСервера = Параметр;
СтруктураДляСервера.Вставить("ДополнительныйФлаг", Истина); // Можем добавить что-то еще перед отправкой
// Вызов серверной процедуры, передавая нашу Структуру
МояСервернаяПроцедура(СтруктураДляСервера);
КонецЕсли;
КонецПроцедуры
// Пример серверной процедуры
&НаСервере
Процедура МояСервернаяПроцедура(ПолученнаяСтруктура)
// Здесь мы можем работать с полученной структурой на сервере
Сообщить("На сервере получена структура.");
Для Каждого КлючЗначение Из ПолученнаяСтруктура Цикл
Сообщить("Ключ: " + КлючЗначение.Ключ + ", Значение: " + КлючЗначение.Значение);
КонецЦикла;
// Например, можно записать данные в регистр сведений или документ:
// МенеджерРегистра = РегистрыСведений.МойРегистр.СоздатьМенеджерЗаписи();
// МенеджерРегистра.Ключ1 = ПолученнаяСтруктура.КлючИзДиалога;
// МенеджерРегистра.Значение = ПолученнаяСтруктура.ЗначениеИзДиалога;
// МенеджерРегистра.Записать();
КонецПроцедуры
При передаче данных с клиента на сервер важно учитывать совместимость типов. Простые типы (строки, числа, даты, булево) и такие составные типы, как Структура, Массив, Соответствие, как правило, передаются без проблем. Однако, если вы попытаетесь передать некоторые мутабельные клиентские объекты, например ТаблицаЗначений или ДеревоЗначений, напрямую на сервер, это может привести к ошибке "Ошибка передачи данных между клиентом и сервером. Значение недопустимого типа".
В таких случаях рекомендуется преобразовывать эти объекты в более "безопасные" для передачи типы:
ТаблицыЗначений: Преобразуйте её в МассивСтруктур на клиенте. Каждая строка таблицы станет структурой в массиве.Структуру или Массив.При вызове серверных процедур (`&НаСервере`) из клиентского кода происходит так называемая контекстная передача. Это означает, что помимо явно указанных параметров, на сервер также автоматически передаются все измененные реквизиты формы (если они были изменены на клиенте с момента предыдущего серверного вызова). Это удобно, так как не нужно вручную передавать каждый измененный реквизит. Однако, это может быть избыточным, если вам нужно просто вызвать серверную функцию, передав ей только несколько параметров, без необходимости синхронизации всего состояния формы.
Для таких случаев существует директива &НаСервереБезКонтекста. Процедуры, помеченные этой директивой, вызываются на сервере без передачи контекста формы. Это может быть полезно для оптимизации производительности при вызове простых серверных функций, не требующих доступа к данным формы. Однако помните, что в такой процедуре у вас не будет доступа к реквизитам формы через Объект или к элементам формы через Элементы.
В нашем случае, при передаче структуры, мы обычно используем &НаСервере, так как нам нужно получить именно эту структуру, а контекст формы может быть полезен для отладки или дополнительных проверок. Однако если ваша серверная процедура абсолютно независима от состояния формы и ей нужны только переданные параметры, рассмотрите возможность использования &НаСервереБезКонтекста.
Передача значений и структуры с клиента на сервер при закрытии немодального диалога в 1С:Предприятии 8.3 — это стандартная задача, решаемая с использованием асинхронных механизмов. Мы рассмотрели два основных подхода: ОписаниеОповещения для обработки результатов асинхронных операций (например, вопросов или открытия форм с возвратом результата) и пару Оповестить()/ОбработкаОповещения() для более гибкой двусторонней связи между формами. В обоих случаях, после получения нужной структуры на клиенте, мы используем вызов серверной процедуры (&НаСервере) для окончательной передачи данных и выполнения серверной логики. Всегда помните о совместимости типов при передаче данных между клиентом и сервером, чтобы избежать ошибок.