Клиентская процедура в модуле формы 1С 8.3: директива, вызов сервера, события

· 10 мин

Содержание

Чтобы создать процедуру на клиенте в 1С 8.3, напишите в модуле формы строку &НаКлиенте прямо над словом Процедура. Без этой директивы платформа скомпилирует процедуру на сервере, и обратиться к элементам формы или показать пользователю вопрос из неё не получится. Обработчики событий удобнее создавать не руками, а из редактора формы: кнопка с лупой у события → «Создать на клиенте».

Типичная ситуация: разработчик дописывает в документ реакцию на смену контрагента, кладёт в обработчик запрос к базе и получает ошибку компиляции. Причина одна: клиентский код не видит базу данных. Всё, что ниже, — про то, как правильно разделить работу между клиентом и сервером в управляемой форме.

Зачем нужны директивы компиляции и где выполняется код

Управляемое приложение 1С 8.3 работает в двух местах. Клиент — это тонкий клиент или браузер на компьютере пользователя. Сервер — кластер 1С или, в файловом варианте, сама клиентская программа, которая играет роль сервера. Директива перед процедурой говорит платформе, где её компилировать.

Директивы модуля формы:

ДирективаГде выполняетсяДоступ к данным формыДоступ к базе
&НаКлиентеНа клиентеДа: Объект, Элементы, реквизиты формыНет
&НаСервереНа сервереДа, данные формы передаются на серверДа
&НаСервереБезКонтекстаНа сервереНетДа
&НаКлиентеНаСервереБезКонтекстаТам, откуда вызвалиНетТолько при вызове с сервера

Главное правило: процедура в модуле формы без директивы считается серверной. Многие ищут ошибку в коде, а её нет — просто забыли строку &НаКлиенте.

Что доступно только на клиенте:

  • 🖥 работа с элементами формы через Элементы: видимость, доступность, заголовки;
  • 💬 диалоги с пользователем: ПоказатьВопрос, ПоказатьПредупреждение, ПоказатьВводЗначения;
  • 📂 открытие других форм через ОткрытьФорму;
  • ⏱ обработчики ожидания: ПодключитьОбработчикОжидания.

Что доступно только на сервере:

  • 🗄 запросы (Новый Запрос);
  • 📄 получение реквизитов ссылки через точку: Объект.Контрагент.ИНН;
  • 🧾 объекты прикладных типов: ДокументОбъект, СправочникОбъект, наборы записей регистров;
  • 📊 ТаблицаЗначений и ДеревоЗначений.

Ссылку на элемент справочника сам по себе клиент знает: её можно хранить в реквизите и передавать на сервер. Недоступны только поля, которые лежат «за ссылкой» в базе.

Синтаксис клиентской процедуры и функции

Минимальная клиентская процедура выглядит так:

&НаКлиенте
Процедура ПроверитьЗаполнение()

	Если Не ЗначениеЗаполнено(Объект.Контрагент) Тогда
		ПоказатьПредупреждение(, "Укажите контрагента");
	КонецЕсли;

КонецПроцедуры

Порядок частей объявления:

  1. Директива &НаКлиенте — на отдельной строке.
  2. Ключевое слово Процедура (или Функция, если нужно вернуть значение).
  3. Имя без пробелов, по смыслу действия: ЗаполнитьПоКонтрагенту, ОбновитьИтоги.
  4. Параметры в скобках через запятую. Знач перед параметром передаёт его по значению; без него — по ссылке.
  5. Необязательный параметр получает значение по умолчанию: Режим = 0.
  6. Слово Экспорт после скобок — если процедуру будут вызывать извне модуля.
  7. Тело и закрывающая строка КонецПроцедуры (КонецФункции).

Клиентская функция возвращает значение через Возврат:

&НаКлиенте
Функция СуммаСтрок()

	Итого = 0;
	Для Каждого Строка Из Объект.Товары Цикл
		Итого = Итого + Строка.Сумма;
	КонецЦикла;
	Возврат Итого;

КонецФункции

Табличная часть Объект.Товары на клиенте доступна: это данные формы, а не объект базы. Поэтому цикл по ней не требует вызова сервера.

Порядок процедур в модуле не важен: клиентская процедура может вызывать другую, объявленную ниже. Важна только директива — клиентская процедура вызывает клиентские и серверные, а серверная клиентскую не видит.

Где ещё бывает клиентский код

Кроме модуля формы, клиентские процедуры пишут:

  • в общих модулях с флажком «Клиент (управляемое приложение)». Директивы в таком модуле не ставят: место выполнения задаёт сам модуль. Процедуру, которую вызывают из форм, помечают Экспорт. По соглашению имя такого модуля заканчивается на Клиент: РаботаСФайламиКлиент;
  • в модуле команды — обработчик ОбработкаКоманды выполняется на клиенте и создаётся с директивой &НаКлиенте;
  • в модуле управляемого приложения — ПередНачаломРаботыСистемы, ПриНачалеРаботыСистемы.

Для кода, общего для клиента и сервера, используют инструкции препроцессора:

#Если Клиент Тогда
	// код только для клиентского контекста
#КонецЕсли

Как создать обработчик события на клиенте через редактор формы

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

  1. Откройте форму объекта в Конфигураторе или EDT.
  2. Во вкладке Элементы выделите нужное поле, например Контрагент.
  3. Откройте палитру свойств (Alt+Enter или двойной щелчок по элементу) и найдите группу «События».
  4. У события ПриИзменении нажмите кнопку с лупой.
  5. В окне выбора типа обработчика выберите «Создать на клиенте».
  6. Платформа откроет модуль формы и вставит заготовку с правильной сигнатурой.

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

Так же создают действие команды формы: вкладка Команды → новая команда → свойство Действие → лупа. Заготовка получит параметр Команда:

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

Если переименовали обработчик в модуле, переименуйте его и в свойстве события. Иначе при открытии формы платформа сообщит, что обработчик не найден.

Работа с реквизитами и элементами формы из клиентского кода

На клиенте разработчик работает с двумя наборами данных, и их легко спутать.

ЧтоКак обращатьсяПример
Основной реквизит (данные объекта)Объект.ИмяРеквизитаОбъект.Дата, Объект.Товары
Реквизит формыПо имени напрямуюПоказыватьАрхив
Элемент формы (визуальный)Элементы.ИмяЭлементаЭлементы.Договор.Видимость
Сама формаЭтотОбъект или ЭтаФормаЭтотОбъект.Модифицированность

Пример: скрыть поле «Договор», пока не выбран контрагент.

&НаКлиенте
Процедура УстановитьВидимостьДоговора()

	Элементы.Договор.Видимость = ЗначениеЗаполнено(Объект.Контрагент);

КонецПроцедуры

Такую процедуру вызывают дважды: в ПриОткрытии и в КонтрагентПриИзменении. Тогда поле ведёт себя одинаково и при открытии, и при редактировании.

Важно различать данные и оформление. Значение реквизита меняют через Объект или имя реквизита. Свойства отображения — через Элементы. Запись Элементы.Контрагент.Значение не сработает: у поля формы нет такого свойства.

Если оформление нужно менять и на клиенте, и при создании формы на сервере, держите логику в одной процедуре &НаКлиентеНаСервереБезКонтекста и передавайте ей форму параметром. Так правило видимости живёт в одном месте.

Вызов сервера из клиентской процедуры

Клиентская процедура не может сама прочитать базу, но может вызвать серверную. Делать это нужно экономно: каждый вызов — это сетевой запрос.

Пример: при смене контрагента подставить договор по умолчанию.

&НаКлиенте
Процедура КонтрагентПриИзменении(Элемент)

	Объект.Договор = ДоговорПоУмолчанию(Объект.Контрагент);
	УстановитьВидимостьДоговора();

КонецПроцедуры

&НаСервереБезКонтекста
Функция ДоговорПоУмолчанию(Контрагент)

	Если Не ЗначениеЗаполнено(Контрагент) Тогда
		Возврат Справочники.ДоговорыКонтрагентов.ПустаяСсылка();
	КонецЕсли;

	Возврат Контрагент.ОсновнойДоговор;

КонецФункции

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

Как выбрать директиву для серверной части:

  • &НаСервереБезКонтекста — когда хватает параметров. На сервер уходят только они, данные формы не передаются. Это самый быстрый вариант.
  • &НаСервере — когда нужно менять сам объект: заполнить табличную часть запросом, вызвать РеквизитФормыВЗначение. Форма уходит на сервер целиком и возвращается обратно.

Что можно передать между клиентом и сервером:

  • ✅ примитивные типы: строка, число, дата, булево;
  • ✅ ссылки на объекты базы;
  • ✅ Структура, Массив, Соответствие и их фиксированные версии с такими же значениями внутри;
  • ❌ ТаблицаЗначений, ДеревоЗначений, Запрос, объекты (ДокументОбъект).

Таблицу значений на сервере превращают в массив структур или кладут в реквизит формы через ЗначениеВРеквизитФормы, и тогда клиент видит её как данные формы.

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

Серверные вызовы из общего модуля

Клиентский общий модуль тоже не видит базу. Ему нужен отдельный общий модуль с флажками «Сервер» и «Вызов сервера». Экспортная процедура такого модуля доступна для вызова с клиента. По соглашению его имя заканчивается на ВызовСервера.

Обработка событий формы: что на клиенте, что на сервере

Платформа сама определяет, где выполняется каждое событие. Директиву у созданной заготовки не меняйте: клиентское событие ждёт клиентскую процедуру.

Событие формыГде выполняетсяТипичная задача
ПриСозданииНаСервереСерверНачальное заполнение, условное оформление
ПриОткрытииКлиентВидимость элементов, подключение обработчиков ожидания
ПередЗаписьюКлиентВопрос пользователю, отказ от записи
ПередЗаписьюНаСервереСерверПроверки по данным базы
ПослеЗаписиКлиентОповещение других форм
ОбработкаОповещенияКлиентРеакция на изменения в других формах
ПередЗакрытиемКлиентПодтверждение закрытия

Отказ от записи на клиенте выглядит так:

&НаКлиенте
Процедура ПередЗаписью(Отказ, ПараметрыЗаписи)

	Если Объект.Товары.Количество() = 0 Тогда
		Отказ = Истина;
		ПоказатьПредупреждение(, "Добавьте хотя бы одну строку в «Товары»");
	КонецЕсли;

КонецПроцедуры

Асинхронные диалоги вместо модальных

В режиме «Такси» модальность по умолчанию запрещена: свойство конфигурации «Режим использования модальности» стоит в значении «Не использовать». Вызов Вопрос() или Предупреждение() тогда даёт ошибку Использование модальных окон в данном режиме запрещено!.

Вариант с описанием оповещения:

&НаКлиенте
Процедура ОчиститьТовары(Команда)

	Оповещение = Новый ОписаниеОповещения("ОчиститьТоварыЗавершение", ЭтотОбъект);
	ПоказатьВопрос(Оповещение, "Очистить табличную часть?", РежимДиалогаВопрос.ДаНет);

КонецПроцедуры

&НаКлиенте
Процедура ОчиститьТоварыЗавершение(Результат, ДополнительныеПараметры) Экспорт

	Если Результат = КодВозвратаДиалога.Да Тогда
		Объект.Товары.Очистить();
	КонецЕсли;

КонецПроцедуры

Процедура-продолжение должна быть клиентской, экспортной и иметь два параметра: результат диалога и дополнительные параметры. Второй аргумент ЭтотОбъект говорит, что искать её нужно в модуле этой формы.

С платформы 8.3.18 доступен второй способ — асинхронные функции с Асинх и Ждать:

&НаКлиенте
Асинх Процедура ОчиститьТоварыАсинх(Команда)

	Ответ = Ждать ВопросАсинх("Очистить табличную часть?", РежимДиалогаВопрос.ДаНет);
	Если Ответ = КодВозвратаДиалога.Да Тогда
		Объект.Товары.Очистить();
	КонецЕсли;

КонецПроцедуры

Код читается сверху вниз, без второй процедуры. Но проверьте режим совместимости конфигурации: в старом режиме ключевые слова Асинх и Ждать не распознаются.

Обработчик ожидания

Процедура для ПодключитьОбработчикОжидания тоже должна быть клиентской и без параметров:

&НаКлиенте
Процедура ПриОткрытии(Отказ)
	ПодключитьОбработчикОжидания("ОбновитьСтатус", 5, Истина);
КонецПроцедуры

&НаКлиенте
Процедура ОбновитьСтатус()
	// вызов сервера и обновление надписи
КонецПроцедуры

Третий параметр Истина означает однократный запуск через 5 секунд. Без него процедура будет вызываться каждые 5 секунд, пока форма открыта.

Ошибки при создании клиентской процедуры и отладка

Диагностика: выберите симптом

Как отлаживать клиентский код:

  1. В Конфигураторе поставьте точку останова на нужной строке клавишей F9.
  2. Запустите отладку клавишей F5 — откроется клиентский сеанс, уже подключённый к отладчику.
  3. Выполните действие на форме. Выполнение остановится на точке.
  4. Шагайте по строкам F10 (без захода в процедуры) или F11 (с заходом).
  5. Смотрите значения в табло (Shift+F9) или наведите курсор на переменную.

Для клиент-серверной базы отладку сервера нужно разрешить отдельно: служба сервера 1С запускается с ключом -debug. Без него точки останова в серверных процедурах не сработают, а клиентские — будут.

Перезапуск службы сервера 1С оборвёт сеансы всех пользователей. Включайте отладку на рабочем сервере только в нерабочее время или ведите отладку на тестовой копии.

Отдельный приём — замер производительности (Отладка → Замер производительности). Он показывает время выполнения каждой строки, в том числе строк с вызовом сервера. Если на одно изменение поля приходится пять серверных вызовов, их стоит объединить в один.

Частые вопросы

Какая директива ставится в модуле формы по умолчанию?
Если директиву не указать, процедура компилируется на сервере, как с директивой &НаСервере.
Можно ли вызвать клиентскую процедуру из серверной?
Нет. Сервер не вызывает клиента. Верните с сервера результат, а клиентскую процедуру вызовите после возврата.
Чем &НаСервере отличается от &НаСервереБезКонтекста?
&НаСервере передаёт на сервер данные всей формы, &НаСервереБезКонтекста — только параметры. Второй вариант быстрее, но не видит реквизиты формы.
Нужно ли слово Экспорт у клиентской процедуры формы?
Только если процедуру вызывают извне: из другой формы через её объект или как продолжение в ОписаниеОповещения. Для вызова внутри модуля оно не нужно.
Где писать клиентский код, общий для нескольких форм?
В общем модуле с флажком «Клиент (управляемое приложение)». Процедуры в нём помечают Экспорт и вызывают через имя модуля.