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

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

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

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

В чем сложность: проблема передачи формы между клиентом и сервером

Попытка передать управляемую форму (параметр ЭтотОбъект или ЭтаФорма) напрямую с клиента на сервер во внеконтекстный метод &НаСервереБезКонтекста закончится ошибкой платформы:

«Ошибка передачи данных между клиентом и сервером. Отсутствует отображение для типа...»

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

Решение задачи: использование директивы &НаКлиентеНаСервереБезКонтекста

Для реализации нашей задачи идеально подходит директива компиляции &НаКлиентеНаСервереБезКонтекста. Этот инструмент позволяет объявить метод, который может компилироваться и выполняться как на стороне клиента, так и на стороне сервера, не имея при этом доступа к неявному контексту формы (к реквизитам и элементам напрямую).

Главный секрет кроется в том, как ведет себя платформа при передаче формы в качестве параметра в такой метод:

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

Реализация кода по шагам

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

Проанализируем программный код, решающий поставленную задачу:


&НаКлиенте
Процедура ВыполнитьПереключение(Команда)
	
	// Шаг 1. Первый вызов - чисто клиентский.
	// Передаем форму "ЭтотОбъект" во внеконтекстный метод. Выполнение происходит на клиенте.
	ПереключитьВидимостьЭлементовБезКонтекста(ЭтотОбъект);
	
	// Шаг 2. Второй вызов - серверный (контекстный).
	ВыполнитьНаСервереСКонтекстом();
	
КонецПроцедуры

&НаСервере
Процедура ВыполнитьНаСервереСКонтекстом()
	
	// Мы зашли на сервер с контекстом формы.
	// Шаг 3. Из серверного контекста вызываем тот же самый внеконтекстный метод.
	ПереключитьВидимостьЭлементовБезКонтекста(ЭтотОбъект);
	
КонецПроцедуры

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

В результате работы этого кода при нажатии на кнопку видимость элементов изменится дважды (сначала на клиенте, затем на сервере), вернувшись в исходное состояние. При этом оба раза отработает один и тот же внеконтекстный метод ПереключитьВидимостьЭлементовБезКонтекста.

Хитрый трюк: как вернуть автодополнение (IntelliSense) в конфигураторе

Разработчики, часто использующие директиву &НаКлиентеНаСервереБезКонтекста, знают об одной неприятной особенности Конфигуратора 1С. Когда вы пишете код внутри такой процедуры и обращаетесь к параметру Форма., система не предлагает контекстную подсказку (список доступных свойств, таких как Элементы, Объект и т.д.), поскольку статический анализатор конфигуратора не знает точный тип передаваемого параметра.

Для обхода этого ограничения в профессиональном сообществе 1С используется хитрость с инструкциями препроцессора. Посмотрим на пример реализации:


#Если Сервер И Не Сервер Тогда
&НаСервере
#Иначе
&НаКлиентеНаСервереБезКонтекста
#КонецЕсли
Процедура ПереключитьВидимостьЭлементовБезКонтекста(Форма)
	
	// Теперь Конфигуратор считает, что это контекстный серверный метод,
	// и предоставляет полноценное автодополнение (IntelliSense) для параметра "Форма".
	Форма.Элементы.ГруппаДляСкрытия.Видимость = Ложь;
	
КонецПроцедуры

Выясним, как это работает: Условие препроцессора #Если Сервер И Не Сервер логически всегда ложно, поэтому при реальной компиляции кода платформой всегда будет выбрана ветка &НаКлиентеНаСервереБезКонтекста. Однако парсер конфигуратора при анализе структуры модуля ориентируется на первую ветку условия, "думая", что процедура является контекстной серверной. Благодаря этому разработчик получает полноценную подсказку при написании кода.

Важное примечание: Данный трюк отлично подходит для классического Конфигуратора 1С, однако при разработке в современной среде 1С:EDT (Enterprise Development Tools) этот метод может приводить к предупреждениям встроенного синтакс-проверка, так как EDT анализирует код более строго.

Альтернативный (рекомендуемый) подход без передачи формы

Хотя передача формы в качестве параметра в метод &НаКлиентеНаСервереБезКонтекста полностью работоспособна, стандарты разработки фирмы "1С" рекомендуют минимизировать передачу сложных интерфейсных объектов. В реальных крупных проектах для оптимизации производительности и разделения логики интерфейса и бизнес-логики часто применяется другой паттерн.

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

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


&НаКлиенте
Процедура ВыполнитьПереключениеАльтернативный(Команда)
	
	// Получаем текущее состояние видимости
	ТекущееСостояние = Элементы.ГруппаДляСкрытия.Видимость;
	
	// Вызываем внеконтекстную функцию, которая просто рассчитывает новое состояние
	НовоеСостояние = ПолучитьНовоеСостояниеВидимости(ТекущееСостояние);
	
	// Применяем результат непосредственно на клиенте
	Элементы.ГруппаДляСкрытия.Видимость = НовоеСостояние;
	
КонецПроцедуры

&НаСервереБезКонтекста
Функция ПолучитьНовоеСостояниеВидимости(ТекущееСостояние)
	
	// Чистая функция без контекста формы и передачи тяжелых объектов.
	// Здесь может быть сложная бизнес-логика проверки прав, ролей или заполненности реквизитов.
	Возврат Не ТекущееСостояние;
	
КонецФункции

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

← На главную