Вызов процедур и функций формы во встроенном языке 1С
Содержание
Процедуру формы вызывают по имени из любого места того же модуля формы. Снаружи — из другой формы, из модуля команды, из обработчика события — метод доступен, только если он помечен словом Экспорт и вы держите ссылку на форму. Главная тонкость — директива компиляции: &НаКлиенте, &НаСервере или &НаСервереБезКонтекста. Клиентский код может вызвать серверный метод (это уходит серверным вызовом), а серверный клиентский — нет.
Ниже — синтаксис вызова, область видимости методов управляемой формы, обращение через ЭтотОбъект и ВладелецФормы, асинхронные вызовы с оповещением о завершении и аккуратная обработка ошибок. Всё — для платформы 8.3 с интерфейсом «Такси».
От директивы зависит, где выполняется процедура
Перед каждой процедурой и функцией модуля формы стоит директива компиляции. Она определяет, на какой стороне — клиент или сервер — выполнится код и какой контекст ему доступен.
- 🖥️
&НаКлиенте— код работает на компьютере пользователя. Отсюда управляют интерфейсом, открывают формы, задают вопросы. - 🗄️
&НаСервере— код работает на сервере, видит реквизиты формы и объект (Объект). Каждый вызов такой процедуры из клиента — это обращение к серверу. - ⚡
&НаСервереБезКонтекста— тоже на сервере, но без доступа к данным формы. Самый быстрый серверный вызов: платформа не передаёт туда и обратно весь контекст формы. - 🔁
&НаКлиентеНаСервереБезКонтекста— процедура компилируется и на клиенте, и на сервере; подходит для чистых расчётов, которые нужны с обеих сторон.
Клиент вызывает сервер напрямую, сервер клиента — нет. Если из &НаСервере попытаться позвать &НаКлиенте-процедуру, конфигурация не соберётся: платформа сообщит, что процедура не найдена в серверном контексте.
Разделение на клиент и сервер — не прихоть, а основа работы управляемого приложения. Интерфейс живёт на клиенте, данные и запросы — на сервере. В веб-клиенте и тонком клиенте каждый серверный вызов реально уходит по сети, поэтому от того, какую директиву вы поставили, напрямую зависит отзывчивость формы. Поставили &НаСервере там, где хватило бы клиентского кода, — пользователь ждёт лишний сетевой round-trip на каждое действие.
&НаКлиенте
Процедура ЗаполнитьПоКнопке(Команда)
// клиент спокойно зовёт серверный метод
ЗагрузитьДанныеНаСервере();
КонецПроцедуры
&НаСервере
Процедура ЗагрузитьДанныеНаСервере()
// здесь доступны реквизиты формы и Объект
Объект.Дата = ТекущаяДата();
КонецПроцедуры
Если серверной процедуре не нужны данные формы, ставьте &НаСервереБезКонтекста — вызов пройдёт заметно быстрее, особенно на формах с большими табличными частями.
Прямой вызов внутри модуля формы
Внутри одного модуля процедуру вызывают просто по имени, со скобками и параметрами. Функцию — там, где нужен результат.
&НаКлиенте
Процедура ОбновитьЗаголовок()
Заголовок = "Заказ № " + Объект.Номер;
КонецПроцедуры
&НаКлиенте
Функция ЕстьНезаполненныеСтроки()
Возврат Объект.Товары.Количество() = 0;
КонецФункции
&НаКлиенте
Процедура НомерПриИзменении(Элемент)
ОбновитьЗаголовок();
Если ЕстьНезаполненныеСтроки() Тогда
Сообщить("Добавьте хотя бы одну строку.");
КонецЕсли;
КонецПроцедуры
Правила простые:
- Клиентская процедура свободно зовёт другую клиентскую.
- Клиентская зовёт серверную — это уходит на сервер.
- Серверная зовёт другую серверную напрямую, без сетевого вызова.
- Параметры передаются по значению (
Знач) или по ссылке. Клиент-серверные вызовы принимают только сериализуемые типы — ссылки, примитивы, структуры; мутабельные объекты вродеТаблицаЗначенийчерез&НаСервереБезКонтекстапередаются копией.
Возвращаемое значение функции использовать напрямую в выражении удобнее, чем заводить лишнюю переменную. Но помните: вызов &НаСервере-функции из цикла на клиенте — это сетевой запрос на каждой итерации.
Параметры в прямом вызове по умолчанию передаются по ссылке — процедура может изменить переданный объект, и изменения увидит вызывающий код. Чтобы защитить значение от изменения, в объявлении параметра пишут Знач:
&НаКлиенте
Процедура ДобавитьСтроку(ТаблицаТоваров)
// меняем переданную таблицу — вызывающий увидит новую строку
НоваяСтрока = ТаблицаТоваров.Добавить();
НоваяСтрока.Количество = 1;
КонецПроцедуры
&НаКлиенте
Процедура НормализоватьКод(Знач Код)
// Знач: локальная копия, оригинал у вызывающего не изменится
Код = СокрЛП(Код);
КонецПроцедуры
Для примитивных типов (число, строка, дата, булево) разница заметна только при записи обратно в параметр. Для таблиц значений, структур и ссылочных объектов отсутствие Знач означает, что процедура работает с тем же объектом, что и вызывающий код.
Экспорт и вызов методов формы снаружи
По умолчанию процедуры формы видны только внутри её модуля. Чтобы метод стал доступен из другого места, добавьте в его объявление Экспорт.
&НаКлиенте
Процедура ПересчитатьИтоги() Экспорт
ОбщаяСумма = Объект.Товары.Итог("Сумма");
КонецПроцедуры
Снаружи метод вызывают через ссылку на форму. Ссылку получают одним из способов:
- 📂 Результат
ОткрытьФорму(...)— переменная-ссылка на открытую форму. - 👪 Свойство
ВладелецФормы— в подчинённой форме указывает на форму-владельца. - 🔗 Параметр, который вы сами передали в другую форму при открытии.
Типичный сценарий — подчинённая форма просит владельца пересчитаться:
&НаКлиенте
Процедура ПрименитьИВернуться(Команда)
Если ТипЗнч(ВладелецФормы) <> Тип("Неопределено") Тогда
ВладелецФормы.ПересчитатьИтоги();
КонецЕсли;
Закрыть();
КонецПроцедуры
Снаружи можно обращаться только к &НаКлиенте-методам, помеченным Экспорт. Серверные процедуры чужой формы недоступны: сервер одной формы не знает про контекст другой. Нужно отдать данные владельцу — делайте это через клиентский экспортный метод, а тот уже сам уйдёт на свой сервер.
Если метод не помечен Экспорт, при обращении снаружи платформа выдаст ошибку, что метод объекта не обнаружен
.
Есть и штатный способ вернуть результат в вызывающую форму без прямого обращения к её методам — через механизм выбора. Форма выбора оповещает о результате вызовом ОповеститьОВыборе(Значение), а форма-владелец ловит его в предопределённом обработчике ОбработкаВыбора:
// в форме выбора
&НаКлиенте
Процедура ВыбратьЭлемент(Команда)
ОповеститьОВыборе(ТекущийЭлемент);
КонецПроцедуры
// в форме-владельце
&НаКлиенте
Процедура ОбработкаВыбора(ВыбранноеЗначение, Источник, СтандартнаяОбработка)
Объект.Контрагент = ВыбранноеЗначение;
КонецПроцедуры
Этот путь надёжнее прямого вызова экспортного метода: вам не нужно хранить ссылку на владельца и заботиться о том, открыт ли он ещё. Выбирайте прямой вызов, когда действительно требуется позвать конкретный метод конкретной формы, и механизм выбора — когда нужно просто вернуть выбранное значение.
Объект ЭтотОбъект — ссылка на саму форму
Внутри модуля формы ЭтотОбъект — это ссылка на текущую форму (тип УправляемаяФорма). Он нужен в трёх случаях:
- передать форму целиком в параметр другого метода или в конструктор оповещения;
- обратиться к реквизиту или методу через точку, когда имя перекрыто локальной переменной;
- явно подчеркнуть, что речь о свойстве формы, а не о переменной.
&НаКлиенте
Процедура ПоказатьВспомогательную(Команда)
Параметры = Новый Структура("Владелец", ЭтотОбъект);
ОткрытьФорму("Обработка.Помощник.Форма", Параметры, ЭтотОбъект);
КонецПроцедуры
Во втором аргументе ОткрытьФорму мы передали структуру параметров, в третьем — ЭтотОбъект как владельца открываемой формы. Теперь в помощнике доступно ВладелецФормы — это наша форма.
ЭтотОбъект чаще всего встречается в конструкторе ОписаниеОповещения — платформе нужно знать, в каком модуле искать процедуру обратного вызова.
Важно не путать два контекста. В модуле формы ЭтотОбъект — это сама управляемая форма, а данные документа или справочника лежат в реквизите Объект. В модуле объекта (модуле документа, справочника) ЭтотОбъект — это уже сам объект данных, а никакой формы там нет. Поэтому код ЭтотОбъект.Товары в модуле формы обратится к реквизиту формы с именем Товары, а в модуле документа — к табличной части документа.
Асинхронные вызовы и ожидание результата
В 8.3 с «Такси» модальные окна отключены, поэтому диалоги и обращения к пользователю работают через оповещение о завершении. Метод не возвращает ответ сразу — он запускает диалог и указывает, какую процедуру вызвать, когда пользователь ответит.
&НаКлиенте
Процедура УдалитьСтроку(Команда)
Оповещение = Новый ОписаниеОповещения("ПослеВопросаОбУдалении", ЭтотОбъект);
ПоказатьВопрос(Оповещение, "Удалить выделенную строку?", РежимДиалогаВопрос.ДаНет);
КонецПроцедуры
&НаКлиенте
Процедура ПослеВопросаОбУдалении(Результат, ДополнительныеПараметры) Экспорт
Если Результат = КодВозвратаДиалога.Да Тогда
УдалитьСтрокуНаСервере();
КонецЕсли;
КонецПроцедуры
Два правила, на которых чаще всего спотыкаются:
- Процедура обратного вызова (
ПослеВопросаОбУдалении) обязана быть сЭкспорт— платформа вызывает её извне текущего кода. - Код после
ПоказатьВопросвыполнится, не дожидаясь ответа. Всё, что должно произойти после выбора пользователя, переносите в процедуру-обработчик.
Начиная с версии платформы не ниже 8.3.18 доступен более линейный синтаксис — Асинх/Ждать. Он читается как обычный последовательный код:
&НаКлиенте
Асинх Процедура УдалитьСтрокуАсинх(Команда)
Ответ = Ждать ВопросАсинх("Удалить выделенную строку?", РежимДиалогаВопрос.ДаНет);
Если Ответ = КодВозвратаДиалога.Да Тогда
УдалитьСтрокуНаСервере();
КонецЕсли;
КонецПроцедуры
Ждать приостанавливает асинхронную процедуру до получения результата, не блокируя интерфейс. Вызывать асинхронные методы и использовать Ждать можно только внутри процедуры, помеченной Асинх.
Если в процедуру обратного вызова нужно передать свои данные — например, индекс удаляемой строки, — у конструктора ОписаниеОповещения есть третий аргумент. Туда передают структуру дополнительных параметров, и она придёт в обработчик вторым параметром ДополнительныеПараметры:
&НаКлиенте
Процедура УдалитьПоИндексу(ИндексСтроки)
Парам = Новый Структура("Индекс", ИндексСтроки);
Оповещение = Новый ОписаниеОповещения("ПослеПодтверждения", ЭтотОбъект, Парам);
ПоказатьВопрос(Оповещение, "Удалить строку?", РежимДиалогаВопрос.ДаНет);
КонецПроцедуры
Так в оповещение заворачивают весь нужный контекст, не заводя реквизитов формы под временное хранение.
Обработка ошибок при вызове методов
Вызов, который может упасть — запись объекта, разбор строки, обращение к внешнему сервису — оборачивают в Попытка … Исключение … КонецПопытки.
&НаСервере
Процедура ЗаписатьНаСервере()
Попытка
Объект.Записать();
Исключение
ИнфОбОшибке = ИнформацияОбОшибке();
Сообщить("Не удалось записать: " + ПодробноеПредставлениеОшибки(ИнфОбОшибке));
КонецПопытки;
КонецПроцедуры
Полезные приёмы:
ИнформацияОбОшибке()возвращает объект с описанием;ПодробноеПредставлениеОшибки()разворачивает его в читаемый текст со стеком.- Внутри
Исключениеможно пробросить ошибку выше вызывающему коду черезВызватьИсключениебез аргументов. - Своё осмысленное сообщение возбуждают как
ВызватьИсключение "Текст ошибки";— вызывающая процедура поймает его в своём блокеПопытка.
Не глушите исключение пустым блоком Исключение … КонецПопытки. Молча проглоченная ошибка превращается в «ничего не работает, а почему — непонятно». Минимум — записывайте её в журнал регистрации.
Частые ошибки компиляции и вызова удобно разбирать по симптому:
Экспорт, либо вы обращаетесь не к той ссылке на форму. Добавьте Экспорт клиентскому методу и проверьте переменную-ссылку.&НаКлиенте-метод. Так нельзя: верните управление на клиент и оттуда зовите клиентский код.Неопределено). Проверьте, что форма открыта и ссылка получена до вызова метода.Производительность: меньше серверных вызовов
Главный тормоз управляемых форм — лишние обращения к серверу. Каждый вызов &НаСервере из клиента — это сетевой запрос с передачей контекста формы туда и обратно.
Что помогает:
- 🧩 Группируйте работу. Вместо пяти серверных вызовов подряд соберите один метод
&НаСервере, который делает всё разом. - 🚫 Не зовите серверные методы в клиентских циклах. Подготовьте данные на клиенте и отправьте одним пакетом.
- ⚡ Выбирайте
&НаСервереБезКонтекста, когда данные формы серверному методу не нужны — платформа не гоняет контекст. - 🧮 Чистую арифметику без обращения к базе выносите в
&НаКлиентеНаСервереБезКонтекстаили в общий модуль — её не нужно считать на сервере.
Выбрать директиву для нового метода помогает простая логика:
&НаКлиенте — работа с пользователем идёт только на клиенте.&НаСервере — ему нужен контекст формы на сервере.&НаСервереБезКонтекста — быстрый серверный вызов без передачи контекста.&НаКлиентеНаСервереБезКонтекста — доступен с обеих сторон, контекст не нужен.Частые вопросы
Чем отличается процедура от функции при вызове?
Возврат и вызывается в выражении (Сумма = ПосчитатьИтог();). Процедура значения не возвращает, её вызывают отдельной строкой.Можно ли из серверной процедуры вызвать клиентскую?
Почему метод чужой формы «не обнаружен», хотя он есть?
Экспорт. Проверьте, что метод помечен Экспорт, имеет директиву &НаКлиенте и вы обращаетесь к правильной ссылке на форму.Как дождаться ответа пользователя в старом стиле, без Асинх?
ОписаниеОповещения: передайте в диалог имя экспортной процедуры обратного вызова и обрабатывайте результат в ней. Код после вызова диалога выполнится сразу, не дожидаясь ответа.Нужен ли ЭтотОбъект для вызова своего же метода?
ЭтотОбъект нужен, только когда форму передают наружу как параметр или когда имя перекрыто локальной переменной.