К статье
Практический материал

Разработка и Конфигурация · 12 мин

1С: как обратиться к реквизиту формы на сервере

Как обратиться к реквизиту формы на сервере в 1С: ДанныеФормыВЗначение, РеквизитФормыВЗначение, типовые ошибки и примеры кода для управляемых форм.

В управляемых формах 1С код выполняется в двух контекстах — на клиенте и на сервере, и реквизиты формы ведут себя в них по-разному. На клиенте реквизит доступен напрямую по имени, а на сервере попытка обратиться к нему «в лоб» часто заканчивается ошибкой или возвратом не тех данных, которые ожидал разработчик. Причина в том, что реквизиты формы — это данные специального типа ДанныеФормыСтруктура, и работа с ними на сервере имеет свои правила.

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

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

Как устроен доступ к реквизитам формы на сервере

Когда в модуле формы объявлена процедура с директивой &НаСервере, внутри неё доступен контекст формы — все её реквизиты видны как свойства. Если реквизит называется Контрагент, то на сервере к нему обращаются просто: Контрагент или ЭтаФорма.Контрагент. Для реквизитов простых типов (строка, число, дата, ссылка) этого достаточно — значение читается и присваивается напрямую.

Сложности начинаются с основным реквизитом формы (обычно называется Объект) и с реквизитами сложных типов — таблицами значений, деревьями, динамическими списками. Основной реквизит на сервере имеет тип ДанныеФормыСтруктура, а не тип прикладного объекта. Это значит, что у него нет методов документа: нельзя вызвать Записать() или Провести() напрямую у Объект.

Прямое обращение к реквизиту по имени

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

&НаСервере

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

// Чтение реквизита формы

ТекущийКонтрагент = Контрагент;

// Запись в реквизит формы

Комментарий = "Заполнено на сервере: " + ТекущийКонтрагент;

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

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

Ограничение способа: так работают только реквизиты, значения которых сериализуются между клиентом и сервером. Ссылочные типы, примитивы, структуры простых данных передаются без проблем. А вот, например, ТабличныйДокумент или COM-объекты в реквизитах формы хранить нельзя вовсе — платформа не даст выбрать такой тип.

Работа с основным реквизитом через РеквизитФормыВЗначение

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

&НаСервере

Процедура ЗаписатьОбъектНаСервере()

ОбъектДанных = РеквизитФормыВЗначение("Объект");

ОбъектДанных.Комментарий = "Изменено на сервере";

ОбъектДанных.Записать();

ЗначениеВРеквизитФормы(ОбъектДанных, "Объект");

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

Здесь важны оба направления преобразования. РеквизитФормыВЗначение создаёт прикладной объект из данных формы, а ЗначениеВРеквизитФормы возвращает изменения обратно, чтобы пользователь увидел их на экране. Если забыть обратное преобразование, объект запишется в базу, но форма продолжит показывать старые данные до следующего перечитывания.

Если методы объекта не нужны — только прочитать или изменить поля — преобразование избыточно. Реквизиты объекта доступны прямо через точку: Объект.Номер, Объект.Товары. Изменение Объект.Комментарий на сервере сразу отразится на форме без всяких преобразований, и это работает быстрее.

Сравнение подходов

Прямое обращение (Объект.Поле)

Подходит для чтения и изменения полей без вызова методов объекта. Быстро, без лишних преобразований. Не позволяет вызвать Записать() или Провести().

РеквизитФормыВЗначение

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

ДанныеФормыВЗначение

Универсальный вариант для произвольного реквизита формы, когда имя передаётся переменной. Используется реже, в основном в общих механизмах.

Обращение к табличной части и таблице значений на форме

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

&НаСервере

Процедура ДобавитьСтрокуНаСервере(НоменклатураСсылка)

НоваяСтрока = Объект.Товары.Добавить();

НоваяСтрока.Номенклатура = НоменклатураСсылка;

НоваяСтрока.Количество = 1;

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

Если на форме есть самостоятельный реквизит типа ТаблицаЗначений (не привязанный к объекту), на сервере он представлен типом ДанныеФормыКоллекция. Перебор строк и добавление работают напрямую, но методы Найти(), НайтиСтроки(), Свернуть() у него отсутствуют. Для полноценной работы таблицу преобразуют: ТЗ = РеквизитФормыВЗначение("МояТаблица").

После массовых изменений таблицы на сервере элемент формы обновляется автоматически при возврате управления на клиент. Явно вызывать обновление отображения обычно не требуется, но если менялись отборы или оформление — проверьте визуальный результат.

Контекстные и бесконтекстные серверные вызовы

Директива &НаСервереБезКонтекста означает, что данные формы в процедуру не передаются — обратиться к реквизиту по имени внутри неё невозможно, попытка закончится ошибкой компиляции. Это осознанное ограничение: бесконтекстный вызов не гоняет состояние формы по сети и работает быстрее.

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

&НаКлиенте

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

ИНН = ПолучитьИНННаСервере(Контрагент);

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

&НаСервереБезКонтекста

Функция ПолучитьИНННаСервере(КонтрагентСсылка)

Возврат КонтрагентСсылка.ИНН;

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

Правило выбора простое: если процедуре нужно менять несколько реквизитов формы или обращаться к основному объекту — используйте контекстный вызов &НаСервере. Если нужно только выполнить запрос к базе по переданным параметрам и вернуть результат — бесконтекстный вызов предпочтительнее по производительности.

Маршрут решения

Как выбрать способ обращения

Нужно прочитать или изменить поле формы без методов объектаОбращайтесь напрямую по имени реквизита в процедуре &НаСервере
Нужно записать или провести объект, вызвать его методыИспользуйте РеквизитФормыВЗначение("Объект") и верните данные через ЗначениеВРеквизитФормы
Нужен только запрос к базе по параметрамСделайте процедуру &НаСервереБезКонтекста и передайте значения параметрами
Нужно искать строки в таблице значений формыПреобразуйте её в ТаблицаЗначений или переберите строки циклом

Типичные ошибки и как их исправить

Самая частая ошибка — «Переменная не определена» при обращении к реквизиту из процедуры с директивой &НаСервереБезКонтекста или из общего модуля. Реквизиты формы существуют только в контексте формы, извне к ним обратиться нельзя в принципе — данные нужно передавать параметрами.

Вторая по популярности проблема — попытка вызвать Объект.Записать() на сервере. Платформа сообщит, что метод не обнаружен, потому что Объект — это ДанныеФормыСтруктура. Решение — преобразование через РеквизитФормыВЗначение, как показано выше.

Третья группа ошибок связана с потерей изменений: разработчик получил объект через РеквизитФормыВЗначение, изменил его, но не вызвал ЗначениеВРеквизитФормы. В базе данные сохранились, а на экране — нет. Симметричная ошибка: преобразование выполнено до того, как пользователь закончил редактирование, и несохранённые данные формы перезаписались старыми значениями объекта.

Типичные ошибки
01
Обращение к реквизиту из &НаСервереБезКонтекста — ошибка компиляции «Переменная не определена». Передайте значение параметром.
02
Вызов Объект.Записать() напрямую — метод отсутствует у ДанныеФормыСтруктура. Сначала выполните РеквизитФормыВЗначение.
03
Забыто обратное преобразование ЗначениеВРеквизитФормы — форма показывает устаревшие данные после записи.
04
Поиск НайтиСтроки() у реквизита-таблицы без преобразования — метод есть только у настоящей ТаблицаЗначений.

Проверка результата и отладка

Убедиться, что обращение к реквизиту на сервере сработало, проще всего по видимому результату: значение поля на форме изменилось, строка в таблице появилась, документ записался. Если форма не обновилась — первым делом проверьте, не потеряно ли обратное преобразование ЗначениеВРеквизитФормы.

При отладке серверных процедур формы ставьте точку останова внутри процедуры &НаСервере и смотрите значения через ЭтаФорма в табло. Тип значения подскажет, с чем вы работаете: ДанныеФормыСтруктура требует преобразования для методов объекта, а ссылочный тип можно использовать сразу.

Полезная практика — выводить диагностические сообщения через Сообщить() на время разработки: имя реквизита, его тип через ТипЗнч() и текущее значение. Это быстро локализует ошибки вроде обращения к переименованному реквизиту или передачи значения не того типа.

Контрольные точки

Как проверить результат

Значение реквизита после серверной процедуры
Норма

Поле на форме отображает новое значение сразу после выполнения

Тревожный признак

Форма показывает старое значение — проверьте ЗначениеВРеквизитФормы

Тип переменной в отладчике
Норма

Для методов объекта тип — ДокументОбъект/СправочникОбъект

Тревожный признак

Тип ДанныеФормыСтруктура при попытке вызвать Записать()

Запись объекта в базе
Норма

Документ проведён/записан, форма не ругается на модифицированность

Тревожный признак

Данные в базе изменились, а форма требует перечитать или конфликтует версий

FAQ

Можно ли обратиться к реквизиту формы из общего модуля?

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

Чем отличаются ДанныеФормыВЗначение и РеквизитФормыВЗначение?

Обе функции преобразуют данные формы в прикладной объект. РеквизитФормыВЗначение принимает имя реквизита строкой и работает с реквизитом верхнего уровня. ДанныеФормыВЗначение принимает само значение данных формы и удобна, когда объект уже получен как значение, например из коллекции. В типовых формах чаще используют РеквизитФормыВЗначение("Объект").

Почему на сервере не виден реквизит, добавленный программно?

Реквизиты, созданные через ИзменитьРеквизиты(), доступны на сервере так же, как созданные в конфигураторе, но только после выполнения этого метода. Если процедура с обращением вызывается до создания реквизита, будет ошибка. Также проверьте, что создание происходит в контекстной серверной процедуре — ИзменитьРеквизиты работает только на сервере.

Как на сервере обратиться к элементу формы, а не к реквизиту?

Через коллекцию Элементы: ЭтаФорма.Элементы.ИмяЭлемента. На сервере доступны не все свойства элементов — например, можно менять видимость, доступность, заголовок, но нельзя работать с фокусом ввода. Помните, что элемент формы и реквизит — разные сущности: элемент только отображает данные реквизита.

Что быстрее: контекстный или бесконтекстный серверный вызов?

Бесконтекстный вызов быстрее, потому что состояние формы не сериализуется и не передаётся на сервер. Разница заметна на «тяжёлых» формах с большими таблицами. Если процедуре нужны лишь пара параметров и запрос к базе — выносите её в &НаСервереБезКонтекста или в общий модуль.

Итог

Что важно запомнить

В серверных процедурах формы реквизиты доступны по имени, но основной реквизит — это ДанныеФормыСтруктура без методов объекта. Для вызова Записать() или Провести() используйте РеквизитФормыВЗначение с обратным преобразованием ЗначениеВРеквизитФормы. В бесконтекстных процедурах реквизиты недоступны — передавайте значения параметрами.