Невозможность передачи формы с клиента на сервер

Иногда я воспринимаю термин «управляемые формы» как иронию. Объектная модель этих форм настолько несогласована, что о какой-то тотальной управляемости можно только мечтать.

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

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

Попытка сделать такую передачу из клиенского модуля — общего или формы приведет к ошибке:

//=== Форма ===

&НаКлиенте
Процедура Тест()
	Сервер.Обработка(ЭтаФорма); //Так не работает
	ТестНаСервере(); //Так работает
КонецПроцедуры

&НаСервере
Процедура ТестНаСервере()
	Сервер.Обработка(ЭтаФорма); //Так работает
КонецПроцедуры

//=== Общий модуль Клиент ===

Процедура Обработка(Форма) Экспорт
	Сервер.Обработка(ЭтаФорма); //Так не работает
КонецПроцедуры

//=== Общий модуль Сервер ===

Процедура Обработка(Форма) Экспорт
	//Тут желаемая обработка формы
КонецПроцедуры

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

Но не всегда это возможно. А если вы хотите часть кода формы вынести в общий модуль, то гарантированно столкнетесь с этой проблемой. Почему гарантированно?

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

//=== Форма ===

&НаКлиенте
Процедура МестоВызова()
	Тест(); //Тут вызывается процедура Тест
КонецПроцедуры


&НаКлиенте
Процедура Тест()
	ТестНаСервере(); 
КонецПроцедуры

&НаСервере
Процедура ТестНаСервере()
КонецПроцедуры

Вы ожидаете, что это можно сделать так:

//=== Форма ===

&НаКлиенте
Процедура МестоВызова()
	Клиент.Тест();
КонецПроцедуры


//=== Общий модуль Клиент ===

Процедура Тест(Форма)
	Сервер.ТестНаСервере(Форма); //Не сработает 
КонецПроцедуры

//=== Общий модуль Сервер ===

&НаСервере
Процедура ТестНаСервере(Форма)
КонецПроцедуры

Но увы, не сработает, с клиента нельзя передать форму в серверный общий модуль.

Приходится выкручиваться примерно так:

//=== Форма ===

&НаКлиенте
Процедура МестоВызова()
	Клиент.Тест();
КонецПроцедуры

&НаСервере
Процедура ВызовПроцедурыТестНаСервере()
	Сервер.ТестНаСервере(ЭтаФорма);
КонецПроцедуры


//=== Общий модуль Клиент ===

Процедура Тест(Форма)
	Форма.ВызовПроцедурыТестНаСервере(); //Сработает 
КонецПроцедуры

//=== Общий модуль Сервер ===

&НаСервере
Процедура ТестНаСервере(Форма)
КонецПроцедуры

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

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

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

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

#Область ДляВызоваССервера
&НаСервере
Процедура ПроцедураНаСервере(ИмяМетода, П1 = "3b779cb1-3a77-4984-8e2b-8c7960a3863a", П2  = "3b779cb1-3a77-4984-8e2b-8c7960a3863a", П3  = "3b779cb1-3a77-4984-8e2b-8c7960a3863a", П4  = "3b779cb1-3a77-4984-8e2b-8c7960a3863a", П5  = "3b779cb1-3a77-4984-8e2b-8c7960a3863a") Экспорт
	П = "";
	Если П1 <> "3b779cb1-3a77-4984-8e2b-8c7960a3863a" Тогда
		П = ", П1";
		Если П2 <> "3b779cb1-3a77-4984-8e2b-8c7960a3863a" Тогда
			П = П + ", П2";
			Если П3 <> "3b779cb1-3a77-4984-8e2b-8c7960a3863a" Тогда
				П = П + ", П3";
				Если П4 <> "3b779cb1-3a77-4984-8e2b-8c7960a3863a" Тогда
					П = П + ", П4";
					Если П5 <> "3b779cb1-3a77-4984-8e2b-8c7960a3863a" Тогда
						П = П + ", П5";
					КонецЕсли;
				КонецЕсли;
			КонецЕсли;
		КонецЕсли;
		
	КонецЕсли;
	Выполнить(ИмяМетода + "(ЭтаФорма" + П + ")");
	
КонецПроцедуры

&НаСервере
Функция ФункцияНаСервере(ИмяМетода, П1 = "3b779cb1-3a77-4984-8e2b-8c7960a3863a", П2  = "3b779cb1-3a77-4984-8e2b-8c7960a3863a", П3  = "3b779cb1-3a77-4984-8e2b-8c7960a3863a", П4  = "3b779cb1-3a77-4984-8e2b-8c7960a3863a", П5  = "3b779cb1-3a77-4984-8e2b-8c7960a3863a") Экспорт
	Р = Неопределено;
	П = "";
	Если П1 <> "3b779cb1-3a77-4984-8e2b-8c7960a3863a" Тогда
		П = ", П1";
		Если П2 <> "3b779cb1-3a77-4984-8e2b-8c7960a3863a" Тогда
			П = П + ", П2";
			Если П3 <> "3b779cb1-3a77-4984-8e2b-8c7960a3863a" Тогда
				П = П + ", П3";
				Если П4 <> "3b779cb1-3a77-4984-8e2b-8c7960a3863a" Тогда
					П = П + ", П4";
					Если П5 <> "3b779cb1-3a77-4984-8e2b-8c7960a3863a" Тогда
						П = П + ", П5";
					КонецЕсли;
				КонецЕсли;
			КонецЕсли;
		КонецЕсли;
		
	КонецЕсли;
	Выполнить("Р = " + ИмяМетода + "(ЭтаФорма" + П + ")");
	Возврат Р;
КонецФункции
#КонецОбласти

Это позволяет вызывать функции из модулей почти привычным образом:

Форма.ПроцедураНаСервере(П1, П2);
Результат = Форма.ФункцияНаСервере(П1);

Этот код поддерживает до 5 параметров. Я сначала хотел использовать NULL для контроля количества параметров, но том решил, что GUID надежнее — NULL может встретиться в качестве значения параметра.

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

Только мне сказали, что 1С использует массив параметров. Мне так не удобно, потому что приходится предварительно создавать массив параметров вместо простого вызова процедуры/функции.

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

fixin

Программирую на 1С с 1999 года. В 1С просто Гений. В 2020 году ушел из офиса на вольные хлеба фриланса. Принимаю заказы.

Читайте также:

комментариев 11

  1. Павел:

    Проблема не большая. Как форму можно отправлять с клиента, если она в общем случае не сериализуется + мутабельность?

    • А тебя не смущает, что форма прекрасно синхронизируется между клиентом и сервером? Платформа прекрасно умеет синхронизировать клиентский и серверный клиент формы.
      Речь как раз о том, чтобы на сервере была доступна серверная версия формы, которая и так уже существует runtime, что не понятного?
      Это искусственное ограничение, непонятно зачем созданное. Наверное, кривая реализация.

  2. Павел:

    Форма «живет» одновременно на клиенте и сервере. На сервере в виде специализированной структуры — отрисовывать ее там не для кого. Синхронизируется далеко не все, а то что может сериализоваться

    • вот именно. поэтому нет никакой проблемы передать ссылку на форму с клиента на сервер. Проблема сугубо надумана архитекторами 1С.

  3. Серг:

    Можете привести более подробный пример по последнему абзацу? Я про добавленные ПроцедураНаСервере, ФункцияНаСервере.

    • Ну например в общем модуле Клиент:

      Вместо:

      Процедура Тест(Форма)
      Сервер.ТестНаСервере(Форма, П1, П2); //Не сработает
      КонецПроцедуры

      Пишем:
      Форма.ПроцедураНаСервере(«ТестНаСервере», П1, П2); //Сработает

  4. Салават:

    В типовых есть Процедура Подключаемый_ВыполнитьКомандуНаСервере(Контекст, Результат) Экспорт, которую можно пнуть с клиента, обладая формой, а дальше вызов идет в ПодключаемыеКоманды.ВыполнитьКоманду, который можно вытащить в расширение, и уже оттуда пнуть свой серверный вызов — форма уже будет на сервере. Возможно появилось недавно. Такие дела в общем.

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

  5. Алексей:

    Через Оповестить можно вызвать, в форме ОБработкаОповещения() прописать что-то типа Выполнить()

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *