Невозможность передачи формы с клиента на сервер
Иногда я воспринимаю термин «управляемые формы» как иронию. Объектная модель этих форм настолько несогласована, что о какой-то тотальной управляемости можно только мечтать.
Сегодня я вскрою одну большую проблему управляемых форм, которая провоцирует писать весь код в одной форме, не разнося его по общим модулям, что снижает повторное использование кода, что очень вредно для проектов, особенно сложных.
Итак, проблема заключается в том, что в параметре процедуры управляемую форму можно передать в серверный общий модуль только из серверной процедуры формы.
Попытка сделать такую передачу из клиенского модуля — общего или формы приведет к ошибке:
//=== Форма === &НаКлиенте Процедура Тест() Сервер.Обработка(ЭтаФорма); //Так не работает ТестНаСервере(); //Так работает КонецПроцедуры &НаСервере Процедура ТестНаСервере() Сервер.Обработка(ЭтаФорма); //Так работает КонецПроцедуры //=== Общий модуль Клиент === Процедура Обработка(Форма) Экспорт Сервер.Обработка(ЭтаФорма); //Так не работает КонецПроцедуры //=== Общий модуль Сервер === Процедура Обработка(Форма) Экспорт //Тут желаемая обработка формы КонецПроцедуры
Многие скажут, что это не проблема, можно ведь вызвать серверную процедуру общего модуля из серверной процедуры формы.
Но не всегда это возможно. А если вы хотите часть кода формы вынести в общий модуль, то гарантированно столкнетесь с этой проблемой. Почему гарантированно?
Допустим вы хотите разнести по модулям такой несложный код:
//=== Форма === &НаКлиенте Процедура МестоВызова() Тест(); //Тут вызывается процедура Тест КонецПроцедуры &НаКлиенте Процедура Тест() ТестНаСервере(); КонецПроцедуры &НаСервере Процедура ТестНаСервере() КонецПроцедуры
Вы ожидаете, что это можно сделать так:
//=== Форма === &НаКлиенте Процедура МестоВызова() Клиент.Тест(); КонецПроцедуры //=== Общий модуль Клиент === Процедура Тест(Форма) Сервер.ТестНаСервере(Форма); //Не сработает КонецПроцедуры //=== Общий модуль Сервер === &НаСервере Процедура ТестНаСервере(Форма) КонецПроцедуры
Но увы, не сработает, с клиента нельзя передать форму в серверный общий модуль.
Приходится выкручиваться примерно так:
//=== Форма === &НаКлиенте Процедура МестоВызова() Клиент.Тест(); КонецПроцедуры &НаСервере Процедура ВызовПроцедурыТестНаСервере() Сервер.ТестНаСервере(ЭтаФорма); КонецПроцедуры //=== Общий модуль Клиент === Процедура Тест(Форма) Форма.ВызовПроцедурыТестНаСервере(); //Сработает КонецПроцедуры //=== Общий модуль Сервер === &НаСервере Процедура ТестНаСервере(Форма) КонецПроцедуры
Причем для вызова каждой процедуры придется написать свою функцию или использовать «магическую» функцию Выполнить. А ведь в форме такой функции нет, поэтому если вы работаете с разными формами, придется в каждую добавлять специальный передаточный код.
Теперь понятно, почему формы в 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С, чтобы возможность передачи формы с клиента на сервер было и из клиентских модулей. Не знаю, реализуют или нет. Это было бы хорошее действие в направлении к компактному, легко модифицируемому и понятному коду.
Проблема не большая. Как форму можно отправлять с клиента, если она в общем случае не сериализуется + мутабельность?
А тебя не смущает, что форма прекрасно синхронизируется между клиентом и сервером? Платформа прекрасно умеет синхронизировать клиентский и серверный клиент формы.
Речь как раз о том, чтобы на сервере была доступна серверная версия формы, которая и так уже существует runtime, что не понятного?
Это искусственное ограничение, непонятно зачем созданное. Наверное, кривая реализация.
Форма «живет» одновременно на клиенте и сервере. На сервере в виде специализированной структуры — отрисовывать ее там не для кого. Синхронизируется далеко не все, а то что может сериализоваться
вот именно. поэтому нет никакой проблемы передать ссылку на форму с клиента на сервер. Проблема сугубо надумана архитекторами 1С.
Можете привести более подробный пример по последнему абзацу? Я про добавленные ПроцедураНаСервере, ФункцияНаСервере.
Ну например в общем модуле Клиент:
Вместо:
Процедура Тест(Форма)
Сервер.ТестНаСервере(Форма, П1, П2); //Не сработает
КонецПроцедуры
Пишем:
Форма.ПроцедураНаСервере(«ТестНаСервере», П1, П2); //Сработает
В типовых есть Процедура Подключаемый_ВыполнитьКомандуНаСервере(Контекст, Результат) Экспорт, которую можно пнуть с клиента, обладая формой, а дальше вызов идет в ПодключаемыеКоманды.ВыполнитьКоманду, который можно вытащить в расширение, и уже оттуда пнуть свой серверный вызов — форма уже будет на сервере. Возможно появилось недавно. Такие дела в общем.
Отлично. 1С все ше дошла до того, что в типовых поправляет недостатки платформы. Забавно на все это смотреть. Героическое сопротивление неизбежному.
Но в принципе, можно и в своем расширении добавлять подобные методы.
бывает что когда долго ноги нет — на ее месте выростает протез 🙂
Через Оповестить можно вызвать, в форме ОБработкаОповещения() прописать что-то типа Выполнить()
Так себе вариант, теряется контекст выполнения.