Как закрыть управляемую форму 1С из серверного кода?

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

В мире разработки на платформе 1С:Предприятие 8.3, особенно при работе с управляемыми формами, мы часто сталкиваемся с архитектурными особенностями, которые требуют особого подхода. Одной из таких особенностей является невозможность напрямую закрыть форму из серверного кода. Формы в управляемом приложении 1С существуют исключительно на стороне клиента, и все операции по программному изменению и управлению их жизненным циклом (открытие, закрытие, изменение видимости (для автоматизации таких задач есть расширение для управления поведением и жизненным циклом управляемой формы)) должны выполняться именно там.

Однако, на практике возникает множество ситуаций, когда решение о закрытии формы принимается на сервере. Например, после выполнения сложной логики проверки данных, записи объекта или выполнения какой-либо серверной процедуры, нам необходимо, чтобы форма автоматически закрылась. В таких случаях мы не можем просто вызвать метод ЭтаФорма.Закрыть() на сервере, так как он доступен только в клиентском контексте. Возникает закономерный вопрос: как обойти это ограничение и реализовать логику закрытия формы, инициированную на сервере?

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

Основной принцип: Сервер принимает решение, клиент выполняет действие

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

Давайте рассмотрим несколько способов реализации этого принципа.

Способ 1: Передача флага с сервера на клиент через параметры (рекомендуемый подход)

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

1.1. Использование ДополнительныхСвойств и ПараметровЗаписи (при записи объекта)

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

  1. Принятие решения на сервере (в модуле объекта): В обработчике события, который выполняется на сервере (например, ПриЗаписи или ОбработкаПроведения для документов), мы устанавливаем специальный флаг в ДополнительныеСвойства объекта.
  2. Передача флага из модуля объекта в модуль формы (сервер): В обработчике формы, который вызывается после записи на сервере (например, ПослеЗаписиНаСервере), мы проверяем этот флаг в ДополнительныеСвойства и передаем его в ПараметрыЗаписи.
  3. Проверка флага и закрытие формы на клиенте: В обработчике формы, который вызывается после записи на клиенте (например, ПослеЗаписи), мы проверяем переданный флаг в ПараметрыЗаписи и, если он установлен, вызываем ЭтаФорма.Закрыть().

Рассмотрим подробный пример, который был предложен в обсуждении:

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

Шаг 1: Модуль объекта (например, справочника)

Здесь мы устанавливаем флаг "Закрывать" в ДополнительныеСвойства. Обратите внимание, что ДополнительныеСвойства объекта доступны в серверных обработчиках модуля объекта.


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

Шаг 2: Модуль формы объекта (серверный контекст)

В этом шаге, после того как объект был записан на сервере, мы перехватываем значение из ДополнительныеСвойства объекта и помещаем его в ПараметрыЗаписи, которые будут переданы в клиентский обработчик.


// Модуль формы объекта (например, ФормаЭлемента справочника Номенклатура)
// &НаСервере
Процедура ПослеЗаписиНаСервере(ТекущийОбъект, ПараметрыЗаписи)
    // Проверяем, был ли установлен флаг "Закрывать" в ДополнительныхСвойствах объекта
    Если ТекущийОбъект.ДополнительныеСвойства.Свойство("Закрывать") Тогда
        ПараметрыЗаписи.Вставить("Закрывать", ТекущийОбъект.ДополнительныеСвойства.Закрывать);
    КонецЕсли;
КонецПроцедуры

Шаг 3: Модуль формы объекта (клиентский контекст)

Наконец, на клиенте, после возвращения из серверной процедуры записи, мы проверяем ПараметрыЗаписи. Если флаг "Закрывать" установлен в Истина, мы вызываем метод Закрыть().


// Модуль формы объекта (например, ФормаЭлемента справочника Номенклатура)
// &НаКлиенте
Процедура ПослеЗаписи(ПараметрыЗаписи)
    Если ПараметрыЗаписи.Свойство("Закрывать") И ПараметрыЗаписи.Закрывать Тогда
        ЭтаФорма.Закрыть();
    КонецЕсли;
КонецПроцедуры

Таким образом, мы успешно передали решение с сервера на клиент, и форма была закрыта клиентским кодом.

1.2. Передача флага через выходные параметры серверной функции/процедуры

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

  1. Вызов серверной процедуры с клиента: Мы вызываем серверную процедуру, которая выполняет необходимую логику.
  2. Установка выходного параметра на сервере: В серверной процедуре, после выполнения логики, мы устанавливаем булевый выходной параметр, указывающий на необходимость закрытия формы.
  3. Проверка выходного параметра на клиенте: После возвращения из серверной процедуры, клиентский код проверяет значение этого параметра и вызывает ЭтаФорма.Закрыть(), если необходимо.

Пример:


// Модуль формы (клиент)
&НаКлиенте
Процедура ВыполнитьПроверкуИЗакрытьФорму()
    НужноЗакрыть = Ложь;
    ВыполнитьСервернуюПроверку(НужноЗакрыть); // Вызываем серверную процедуру с передачей параметра по ссылке
    Если НужноЗакрыть Тогда
        ЭтаФорма.Закрыть();
    КонецЕсли;
КонецПроцедуры

// Модуль формы (сервер)
&НаСервере
Процедура ВыполнитьСервернуюПроверку(НужноЗакрыть)
    // Здесь выполняется какая-то сложная логика на сервере
    // Например, проверка пароля, прав доступа или наличия данных
    ПереданныйПароль = "12345678"; // Допустим, пароль был передан с клиента
    ПравильныйПароль = "12345678";

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

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

1.3. Использование ПараметровОткрытия и ПриОткрытии (для первоначального открытия формы)

Если решение о закрытии формы принимается еще до ее полноценного открытия (например, сразу после попытки открыть), мы можем использовать ПараметрыОткрытия.

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

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

Шаг 1: Форма, которая вызывает открытие другой формы (клиент)

В этой форме мы будем открывать нашу целевую форму и передавать ей параметры.


// Например, в модуле команды какой-либо формы
&НаКлиенте
Процедура ОткрытьФормуСПаролем(Команда)
    ПараметрыДляОткрытия = Новый Структура("Пароль", "12345678"); // Пример передачи пароля
    ОткрытьФорму("ВнешняяОбработка.МояОбработка.Форма.Форма", ПараметрыДляОткрытия, ЭтаФорма);
КонецПроцедуры

Шаг 2: Модуль целевой формы (серверный контекст)

Мы используем ПриСозданииНаСервере для выполнения серверной проверки. Однако, вместо попытки закрыть форму (что, как мы выяснили, невозможно), мы можем либо изменить свойства формы, либо установить специальный флаг, который будет прочитан на клиенте.

Особо хотим отметить, что ПриСозданииНаСервере имеет параметры Отказ и СтандартнаяОбработка. Установка Отказ = Истина в этой процедуре приведет к тому, что форма не будет открыта вовсе. Это может быть воспринято как "закрытие" до ее появления. Этот подход мы рассмотрим подробнее в следующем разделе. Если же нам все-таки нужно, чтобы форма открылась, но потом сразу закрылась, то продолжаем использовать флаги.


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

Шаг 3: Модуль целевой формы (клиентский контекст)

После того как серверная процедура ПриСозданииНаСервере завершилась, управление возвращается на клиент, и срабатывает ПриОткрытии.


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

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

Способ 2: Предотвращение открытия формы (Отказ = Истина в ПриСозданииНаСервере)

Этот способ отличается тем, что он не "закрывает" уже открытую форму, а предотвращает ее открытие в принципе — для этого подойдёт обработка для настройки серверных проверок ввода данных без программирования. Он очень полезен, когда решение о том, должна ли форма быть показана пользователю, принимается на самой ранней стадии ее создания на сервере.

Обработчик события ПриСозданииНаСервере имеет параметр Отказ. Если мы установим Отказ = Истина в этом серверном обработчике, форма не будет открыта. Это идеальный вариант для сценариев, где, например, на основании данных сервера (прав пользователя, наличия критических данных, проверки лицензии или, как в примере автора, проверки пароля) мы решаем, что форма вообще не должна быть доступна.

Рассмотрим пример с проверкой пароля, но уже с использованием Отказ:


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

    ПравильныйПароль = "12345678";

    Если ПереданныйПароль = ПравильныйПароль Тогда
        Сообщить("Пароль верный. Форма будет открыта.");
        Отказ = Ложь; // Убеждаемся, что форма откроется
    Иначе
        Сообщить("Неправильный пароль! Открытие формы отменено.");
        Отказ = Истина; // Отменяем открытие формы
    КонецЕсли;
КонецПроцедуры

В этом случае, если пароль не совпадает, форма просто не появится на экране пользователя, что с точки зрения пользователя выглядит как мгновенное "закрытие" или отмена открытия.

Способ 3: Грубые, но иногда "рабочие" методы (НЕ РЕКОМЕНДУЕТСЯ)

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

3.1. Вызов исключения (ВызватьИсключение)

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

Пример (не рекомендуется):


// Модуль формы (сервер)
&НаСервере
Процедура МояСервернаяПроцедура()
    // Какая-то логика
    Если УсловиеДляОшибки Тогда
        ВызватьИсключение "Не удалось выполнить операцию. Форма будет закрыта.";
    КонецЕсли;
    // Продолжение логики
КонецПроцедуры

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

3.2. "Сломать" форму через ошибку (конфликт счетчиков данных)

Как было упомянуто в одном из сообщений, теоретически можно вызвать ошибку, связанную с несоответствием счетчиков данных между клиентом и сервером (например, "Различаются значения счетчиков для данных форм: 2.4 != 1.2"). Такие ошибки могут привести к тому, что платформа автоматически закроет форму из-за невозможности синхронизации ее состояния.

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

Заключение

Мы рассмотрели различные подходы к решению задачи закрытия управляемой формы 1С, когда решение об этом принимается на сервере. Самый надежный, гибкий и рекомендованный способ – это передача флага или параметра с сервера на клиент, где уже клиентский код выполняет действие ЭтаФорма.Закрыть().

Особенно эффективны методы с использованием ДополнительныеСвойства и ПараметрыЗаписи для объектов или передача выходных параметров в серверные функции. Для контроля открытия формы с самого начала, мы можем использовать параметр Отказ в процедуре ПриСозданииНаСервере.

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

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

← На главную