Клиентская процедура в модуле формы 1С 8.3: директива, вызов сервера, события
Содержание
Чтобы создать процедуру на клиенте в 1С 8.3, напишите в модуле формы строку &НаКлиенте прямо над словом Процедура. Без этой директивы платформа скомпилирует процедуру на сервере, и обратиться к элементам формы или показать пользователю вопрос из неё не получится. Обработчики событий удобнее создавать не руками, а из редактора формы: кнопка с лупой у события → «Создать на клиенте».
Типичная ситуация: разработчик дописывает в документ реакцию на смену контрагента, кладёт в обработчик запрос к базе и получает ошибку компиляции. Причина одна: клиентский код не видит базу данных. Всё, что ниже, — про то, как правильно разделить работу между клиентом и сервером в управляемой форме.
Зачем нужны директивы компиляции и где выполняется код
Управляемое приложение 1С 8.3 работает в двух местах. Клиент — это тонкий клиент или браузер на компьютере пользователя. Сервер — кластер 1С или, в файловом варианте, сама клиентская программа, которая играет роль сервера. Директива перед процедурой говорит платформе, где её компилировать.
Директивы модуля формы:
| Директива | Где выполняется | Доступ к данным формы | Доступ к базе |
|---|---|---|---|
&НаКлиенте | На клиенте | Да: Объект, Элементы, реквизиты формы | Нет |
&НаСервере | На сервере | Да, данные формы передаются на сервер | Да |
&НаСервереБезКонтекста | На сервере | Нет | Да |
&НаКлиентеНаСервереБезКонтекста | Там, откуда вызвали | Нет | Только при вызове с сервера |
Главное правило: процедура в модуле формы без директивы считается серверной. Многие ищут ошибку в коде, а её нет — просто забыли строку &НаКлиенте.
Что доступно только на клиенте:
- 🖥 работа с элементами формы через
Элементы: видимость, доступность, заголовки; - 💬 диалоги с пользователем:
ПоказатьВопрос,ПоказатьПредупреждение,ПоказатьВводЗначения; - 📂 открытие других форм через
ОткрытьФорму; - ⏱ обработчики ожидания:
ПодключитьОбработчикОжидания.
Что доступно только на сервере:
- 🗄 запросы (
Новый Запрос); - 📄 получение реквизитов ссылки через точку:
Объект.Контрагент.ИНН; - 🧾 объекты прикладных типов:
ДокументОбъект,СправочникОбъект, наборы записей регистров; - 📊
ТаблицаЗначенийиДеревоЗначений.
Ссылку на элемент справочника сам по себе клиент знает: её можно хранить в реквизите и передавать на сервер. Недоступны только поля, которые лежат «за ссылкой» в базе.
Синтаксис клиентской процедуры и функции
Минимальная клиентская процедура выглядит так:
&НаКлиенте
Процедура ПроверитьЗаполнение()
Если Не ЗначениеЗаполнено(Объект.Контрагент) Тогда
ПоказатьПредупреждение(, "Укажите контрагента");
КонецЕсли;
КонецПроцедуры
Порядок частей объявления:
- Директива
&НаКлиенте— на отдельной строке. - Ключевое слово
Процедура(илиФункция, если нужно вернуть значение). - Имя без пробелов, по смыслу действия:
ЗаполнитьПоКонтрагенту,ОбновитьИтоги. - Параметры в скобках через запятую.
Значперед параметром передаёт его по значению; без него — по ссылке. - Необязательный параметр получает значение по умолчанию:
Режим = 0. - Слово
Экспортпосле скобок — если процедуру будут вызывать извне модуля. - Тело и закрывающая строка
КонецПроцедуры(КонецФункции).
Клиентская функция возвращает значение через Возврат:
&НаКлиенте
Функция СуммаСтрок()
Итого = 0;
Для Каждого Строка Из Объект.Товары Цикл
Итого = Итого + Строка.Сумма;
КонецЦикла;
Возврат Итого;
КонецФункции
Табличная часть Объект.Товары на клиенте доступна: это данные формы, а не объект базы. Поэтому цикл по ней не требует вызова сервера.
Порядок процедур в модуле не важен: клиентская процедура может вызывать другую, объявленную ниже. Важна только директива — клиентская процедура вызывает клиентские и серверные, а серверная клиентскую не видит.
Где ещё бывает клиентский код
Кроме модуля формы, клиентские процедуры пишут:
- в общих модулях с флажком «Клиент (управляемое приложение)». Директивы в таком модуле не ставят: место выполнения задаёт сам модуль. Процедуру, которую вызывают из форм, помечают
Экспорт. По соглашению имя такого модуля заканчивается наКлиент:РаботаСФайламиКлиент; - в модуле команды — обработчик
ОбработкаКомандывыполняется на клиенте и создаётся с директивой&НаКлиенте; - в модуле управляемого приложения —
ПередНачаломРаботыСистемы,ПриНачалеРаботыСистемы.
Для кода, общего для клиента и сервера, используют инструкции препроцессора:
#Если Клиент Тогда
// код только для клиентского контекста
#КонецЕсли
Как создать обработчик события на клиенте через редактор формы
Писать обработчик руками можно, но платформа должна ещё «связать» его с событием. Надёжнее сделать это из конструктора:
- Откройте форму объекта в Конфигураторе или EDT.
- Во вкладке
Элементывыделите нужное поле, напримерКонтрагент. - Откройте палитру свойств (
Alt+Enterили двойной щелчок по элементу) и найдите группу «События». - У события
ПриИзменениинажмите кнопку с лупой. - В окне выбора типа обработчика выберите «Создать на клиенте».
- Платформа откроет модуль формы и вставит заготовку с правильной сигнатурой.
В том же окне есть ещё два варианта: «Создать на клиенте и процедуру на сервере» и «Создать на клиенте и процедуру на сервере без контекста». Второй вариант сразу даёт пару: клиентский обработчик и серверную процедуру, которую он вызывает. Это экономит время, когда в обработчике нужна база.
Так же создают действие команды формы: вкладка Команды → новая команда → свойство Действие → лупа. Заготовка получит параметр Команда:
&НаКлиенте
Процедура ЗаполнитьТовары(Команда)
ЗаполнитьТоварыНаСервере();
КонецПроцедуры
Если переименовали обработчик в модуле, переименуйте его и в свойстве события. Иначе при открытии формы платформа сообщит, что обработчик не найден.
Работа с реквизитами и элементами формы из клиентского кода
На клиенте разработчик работает с двумя наборами данных, и их легко спутать.
| Что | Как обращаться | Пример |
|---|---|---|
| Основной реквизит (данные объекта) | Объект.ИмяРеквизита | Объект.Дата, Объект.Товары |
| Реквизит формы | По имени напрямую | ПоказыватьАрхив |
| Элемент формы (визуальный) | Элементы.ИмяЭлемента | Элементы.Договор.Видимость |
| Сама форма | ЭтотОбъект или ЭтаФорма | ЭтотОбъект.Модифицированность |
Пример: скрыть поле «Договор», пока не выбран контрагент.
&НаКлиенте
Процедура УстановитьВидимостьДоговора()
Элементы.Договор.Видимость = ЗначениеЗаполнено(Объект.Контрагент);
КонецПроцедуры
Такую процедуру вызывают дважды: в ПриОткрытии и в КонтрагентПриИзменении. Тогда поле ведёт себя одинаково и при открытии, и при редактировании.
Важно различать данные и оформление. Значение реквизита меняют через Объект или имя реквизита. Свойства отображения — через Элементы. Запись Элементы.Контрагент.Значение не сработает: у поля формы нет такого свойства.
Если оформление нужно менять и на клиенте, и при создании формы на сервере, держите логику в одной процедуре &НаКлиентеНаСервереБезКонтекста и передавайте ей форму параметром. Так правило видимости живёт в одном месте.
Вызов сервера из клиентской процедуры
Клиентская процедура не может сама прочитать базу, но может вызвать серверную. Делать это нужно экономно: каждый вызов — это сетевой запрос.
Пример: при смене контрагента подставить договор по умолчанию.
&НаКлиенте
Процедура КонтрагентПриИзменении(Элемент)
Объект.Договор = ДоговорПоУмолчанию(Объект.Контрагент);
УстановитьВидимостьДоговора();
КонецПроцедуры
&НаСервереБезКонтекста
Функция ДоговорПоУмолчанию(Контрагент)
Если Не ЗначениеЗаполнено(Контрагент) Тогда
Возврат Справочники.ДоговорыКонтрагентов.ПустаяСсылка();
КонецЕсли;
Возврат Контрагент.ОсновнойДоговор;
КонецФункции
Здесь ОсновнойДоговор — реквизит справочника, его имя в вашей конфигурации может быть другим. Смысл примера в другом: функция без контекста получила только ссылку и вернула только ссылку.
Как выбрать директиву для серверной части:
&НаСервереБезКонтекста— когда хватает параметров. На сервер уходят только они, данные формы не передаются. Это самый быстрый вариант.&НаСервере— когда нужно менять сам объект: заполнить табличную часть запросом, вызватьРеквизитФормыВЗначение. Форма уходит на сервер целиком и возвращается обратно.
Что можно передать между клиентом и сервером:
- ✅ примитивные типы: строка, число, дата, булево;
- ✅ ссылки на объекты базы;
- ✅
Структура,Массив,Соответствиеи их фиксированные версии с такими же значениями внутри; - ❌
ТаблицаЗначений,ДеревоЗначений,Запрос, объекты (ДокументОбъект).
Таблицу значений на сервере превращают в массив структур или кладут в реквизит формы через ЗначениеВРеквизитФормы, и тогда клиент видит её как данные формы.
Если в одном обработчике нужно три значения с сервера, верните одну структуру за один вызов, а не делайте три вызова подряд. На медленном канале разница ощутима для пользователя.
Серверные вызовы из общего модуля
Клиентский общий модуль тоже не видит базу. Ему нужен отдельный общий модуль с флажками «Сервер» и «Вызов сервера». Экспортная процедура такого модуля доступна для вызова с клиента. По соглашению его имя заканчивается на ВызовСервера.
Обработка событий формы: что на клиенте, что на сервере
Платформа сама определяет, где выполняется каждое событие. Директиву у созданной заготовки не меняйте: клиентское событие ждёт клиентскую процедуру.
| Событие формы | Где выполняется | Типичная задача |
|---|---|---|
ПриСозданииНаСервере | Сервер | Начальное заполнение, условное оформление |
ПриОткрытии | Клиент | Видимость элементов, подключение обработчиков ожидания |
ПередЗаписью | Клиент | Вопрос пользователю, отказ от записи |
ПередЗаписьюНаСервере | Сервер | Проверки по данным базы |
ПослеЗаписи | Клиент | Оповещение других форм |
ОбработкаОповещения | Клиент | Реакция на изменения в других формах |
ПередЗакрытием | Клиент | Подтверждение закрытия |
Отказ от записи на клиенте выглядит так:
&НаКлиенте
Процедура ПередЗаписью(Отказ, ПараметрыЗаписи)
Если Объект.Товары.Количество() = 0 Тогда
Отказ = Истина;
ПоказатьПредупреждение(, "Добавьте хотя бы одну строку в «Товары»");
КонецЕсли;
КонецПроцедуры
Асинхронные диалоги вместо модальных
В режиме «Такси» модальность по умолчанию запрещена: свойство конфигурации «Режим использования модальности» стоит в значении «Не использовать». Вызов Вопрос() или Предупреждение() тогда даёт ошибку Использование модальных окон в данном режиме запрещено!.
Вариант с описанием оповещения:
&НаКлиенте
Процедура ОчиститьТовары(Команда)
Оповещение = Новый ОписаниеОповещения("ОчиститьТоварыЗавершение", ЭтотОбъект);
ПоказатьВопрос(Оповещение, "Очистить табличную часть?", РежимДиалогаВопрос.ДаНет);
КонецПроцедуры
&НаКлиенте
Процедура ОчиститьТоварыЗавершение(Результат, ДополнительныеПараметры) Экспорт
Если Результат = КодВозвратаДиалога.Да Тогда
Объект.Товары.Очистить();
КонецЕсли;
КонецПроцедуры
Процедура-продолжение должна быть клиентской, экспортной и иметь два параметра: результат диалога и дополнительные параметры. Второй аргумент ЭтотОбъект говорит, что искать её нужно в модуле этой формы.
С платформы 8.3.18 доступен второй способ — асинхронные функции с Асинх и Ждать:
&НаКлиенте
Асинх Процедура ОчиститьТоварыАсинх(Команда)
Ответ = Ждать ВопросАсинх("Очистить табличную часть?", РежимДиалогаВопрос.ДаНет);
Если Ответ = КодВозвратаДиалога.Да Тогда
Объект.Товары.Очистить();
КонецЕсли;
КонецПроцедуры
Код читается сверху вниз, без второй процедуры. Но проверьте режим совместимости конфигурации: в старом режиме ключевые слова Асинх и Ждать не распознаются.
Обработчик ожидания
Процедура для ПодключитьОбработчикОжидания тоже должна быть клиентской и без параметров:
&НаКлиенте
Процедура ПриОткрытии(Отказ)
ПодключитьОбработчикОжидания("ОбновитьСтатус", 5, Истина);
КонецПроцедуры
&НаКлиенте
Процедура ОбновитьСтатус()
// вызов сервера и обновление надписи
КонецПроцедуры
Третий параметр Истина означает однократный запуск через 5 секунд. Без него процедура будет вызываться каждые 5 секунд, пока форма открыта.
Ошибки при создании клиентской процедуры и отладка
Как отлаживать клиентский код:
- В Конфигураторе поставьте точку останова на нужной строке клавишей
F9. - Запустите отладку клавишей
F5— откроется клиентский сеанс, уже подключённый к отладчику. - Выполните действие на форме. Выполнение остановится на точке.
- Шагайте по строкам
F10(без захода в процедуры) илиF11(с заходом). - Смотрите значения в табло (
Shift+F9) или наведите курсор на переменную.
Для клиент-серверной базы отладку сервера нужно разрешить отдельно: служба сервера 1С запускается с ключом -debug. Без него точки останова в серверных процедурах не сработают, а клиентские — будут.
Перезапуск службы сервера 1С оборвёт сеансы всех пользователей. Включайте отладку на рабочем сервере только в нерабочее время или ведите отладку на тестовой копии.
Отдельный приём — замер производительности (Отладка → Замер производительности). Он показывает время выполнения каждой строки, в том числе строк с вызовом сервера. Если на одно изменение поля приходится пять серверных вызовов, их стоит объединить в один.