Разработка и Конфигурация · 12 мин
1С: как обратиться к реквизиту формы на сервере
Как обратиться к реквизиту формы на сервере в 1С: ДанныеФормыВЗначение, РеквизитФормыВЗначение, типовые ошибки и примеры кода для управляемых форм.
В управляемых формах 1С код выполняется в двух контекстах — на клиенте и на сервере, и реквизиты формы ведут себя в них по-разному. На клиенте реквизит доступен напрямую по имени, а на сервере попытка обратиться к нему «в лоб» часто заканчивается ошибкой или возвратом не тех данных, которые ожидал разработчик. Причина в том, что реквизиты формы — это данные специального типа ДанныеФормыСтруктура, и работа с ними на сервере имеет свои правила.
Чаще всего задача звучит так: в серверной процедуре нужно прочитать или изменить реквизит формы — например, объект документа, таблицу значений или простое поле ввода. В зависимости от типа реквизита и цели обращения применяются разные подходы: прямое обращение по имени, преобразование через РеквизитФормыВЗначение или работа с основным реквизитом через Объект.
Ниже разобраны все рабочие способы с примерами кода, типичные ошибки компиляции и выполнения, а также критерии выбора подхода под конкретную задачу.
Как устроен доступ к реквизитам формы на сервере
Когда в модуле формы объявлена процедура с директивой &НаСервере, внутри неё доступен контекст формы — все её реквизиты видны как свойства. Если реквизит называется Контрагент, то на сервере к нему обращаются просто: Контрагент или ЭтаФорма.Контрагент. Для реквизитов простых типов (строка, число, дата, ссылка) этого достаточно — значение читается и присваивается напрямую.
Сложности начинаются с основным реквизитом формы (обычно называется Объект) и с реквизитами сложных типов — таблицами значений, деревьями, динамическими списками. Основной реквизит на сервере имеет тип ДанныеФормыСтруктура, а не тип прикладного объекта. Это значит, что у него нет методов документа: нельзя вызвать Записать() или Провести() напрямую у Объект.
Прямое обращение к реквизиту по имени
Простейший сценарий — серверная процедура читает или устанавливает значение реквизита формы. Директива компиляции указывается явно, обращение идёт по имени реквизита:
&НаСервере
Процедура ЗаполнитьКомментарийНаСервере()
// Чтение реквизита формы
ТекущийКонтрагент = Контрагент;
// Запись в реквизит формы
Комментарий = "Заполнено на сервере: " + ТекущийКонтрагент;
КонецПроцедуры
Обратите внимание: внутри серверной процедуры формы обращение ЭтаФорма тоже работает, но в методах самой формы имена реквизитов разрешаются и без него. Явное указание ЭтаФорма.Реквизит делает код читаемее и защищает от конфликтов имён с локальными переменными.
Ограничение способа: так работают только реквизиты, значения которых сериализуются между клиентом и сервером. Ссылочные типы, примитивы, структуры простых данных передаются без проблем. А вот, например, ТабличныйДокумент или COM-объекты в реквизитах формы хранить нельзя вовсе — платформа не даст выбрать такой тип.
Работа с основным реквизитом через РеквизитФормыВЗначение
Когда на сервере нужен полноценный прикладной объект — чтобы вызвать его методы, обратиться к табличным частям как к табличным частям объекта или записать данные в базу — используется преобразование:
&НаСервере
Процедура ЗаписатьОбъектНаСервере()
ОбъектДанных = РеквизитФормыВЗначение("Объект");
ОбъектДанных.Комментарий = "Изменено на сервере";
ОбъектДанных.Записать();
ЗначениеВРеквизитФормы(ОбъектДанных, "Объект");
КонецПроцедуры
Здесь важны оба направления преобразования. РеквизитФормыВЗначение создаёт прикладной объект из данных формы, а ЗначениеВРеквизитФормы возвращает изменения обратно, чтобы пользователь увидел их на экране. Если забыть обратное преобразование, объект запишется в базу, но форма продолжит показывать старые данные до следующего перечитывания.
Если методы объекта не нужны — только прочитать или изменить поля — преобразование избыточно. Реквизиты объекта доступны прямо через точку: Объект.Номер, Объект.Товары. Изменение Объект.Комментарий на сервере сразу отразится на форме без всяких преобразований, и это работает быстрее.
Сравнение подходов
Подходит для чтения и изменения полей без вызова методов объекта. Быстро, без лишних преобразований. Не позволяет вызвать Записать() или Провести().
Нужен, когда требуются методы прикладного объекта: запись, проведение, обработка заполнения. Дороже по ресурсам, требует обратного преобразования.
Универсальный вариант для произвольного реквизита формы, когда имя передаётся переменной. Используется реже, в основном в общих механизмах.
Обращение к табличной части и таблице значений на форме
Табличная часть объекта на сервере доступна через Объект.ИмяТабличнойЧасти. С ней можно работать почти как обычно: перебирать строки, добавлять и удалять. Пример заполнения табличной части на сервере:
&НаСервере
Процедура ДобавитьСтрокуНаСервере(НоменклатураСсылка)
НоваяСтрока = Объект.Товары.Добавить();
НоваяСтрока.Номенклатура = НоменклатураСсылка;
НоваяСтрока.Количество = 1;
КонецПроцедуры
Если на форме есть самостоятельный реквизит типа ТаблицаЗначений (не привязанный к объекту), на сервере он представлен типом ДанныеФормыКоллекция. Перебор строк и добавление работают напрямую, но методы Найти(), НайтиСтроки(), Свернуть() у него отсутствуют. Для полноценной работы таблицу преобразуют: ТЗ = РеквизитФормыВЗначение("МояТаблица").
После массовых изменений таблицы на сервере элемент формы обновляется автоматически при возврате управления на клиент. Явно вызывать обновление отображения обычно не требуется, но если менялись отборы или оформление — проверьте визуальный результат.
Контекстные и бесконтекстные серверные вызовы
Директива &НаСервереБезКонтекста означает, что данные формы в процедуру не передаются — обратиться к реквизиту по имени внутри неё невозможно, попытка закончится ошибкой компиляции. Это осознанное ограничение: бесконтекстный вызов не гоняет состояние формы по сети и работает быстрее.
Если бесконтекстной процедуре нужны данные реквизита, их передают параметрами. Например, вместо чтения реквизита Контрагент на сервере клиентская процедура передаёт значение:
&НаКлиенте
Процедура КонтрагентПриИзменении(Элемент)
ИНН = ПолучитьИНННаСервере(Контрагент);
КонецПроцедуры
&НаСервереБезКонтекста
Функция ПолучитьИНННаСервере(КонтрагентСсылка)
Возврат КонтрагентСсылка.ИНН;
КонецФункции
Правило выбора простое: если процедуре нужно менять несколько реквизитов формы или обращаться к основному объекту — используйте контекстный вызов &НаСервере. Если нужно только выполнить запрос к базе по переданным параметрам и вернуть результат — бесконтекстный вызов предпочтительнее по производительности.
Как выбрать способ обращения
Типичные ошибки и как их исправить
Самая частая ошибка — «Переменная не определена» при обращении к реквизиту из процедуры с директивой &НаСервереБезКонтекста или из общего модуля. Реквизиты формы существуют только в контексте формы, извне к ним обратиться нельзя в принципе — данные нужно передавать параметрами.
Вторая по популярности проблема — попытка вызвать Объект.Записать() на сервере. Платформа сообщит, что метод не обнаружен, потому что Объект — это ДанныеФормыСтруктура. Решение — преобразование через РеквизитФормыВЗначение, как показано выше.
Третья группа ошибок связана с потерей изменений: разработчик получил объект через РеквизитФормыВЗначение, изменил его, но не вызвал ЗначениеВРеквизитФормы. В базе данные сохранились, а на экране — нет. Симметричная ошибка: преобразование выполнено до того, как пользователь закончил редактирование, и несохранённые данные формы перезаписались старыми значениями объекта.
Проверка результата и отладка
Убедиться, что обращение к реквизиту на сервере сработало, проще всего по видимому результату: значение поля на форме изменилось, строка в таблице появилась, документ записался. Если форма не обновилась — первым делом проверьте, не потеряно ли обратное преобразование ЗначениеВРеквизитФормы.
При отладке серверных процедур формы ставьте точку останова внутри процедуры &НаСервере и смотрите значения через ЭтаФорма в табло. Тип значения подскажет, с чем вы работаете: ДанныеФормыСтруктура требует преобразования для методов объекта, а ссылочный тип можно использовать сразу.
Полезная практика — выводить диагностические сообщения через Сообщить() на время разработки: имя реквизита, его тип через ТипЗнч() и текущее значение. Это быстро локализует ошибки вроде обращения к переименованному реквизиту или передачи значения не того типа.
Как проверить результат
Поле на форме отображает новое значение сразу после выполнения
Форма показывает старое значение — проверьте ЗначениеВРеквизитФормы
Для методов объекта тип — ДокументОбъект/СправочникОбъект
Тип ДанныеФормыСтруктура при попытке вызвать Записать()
Документ проведён/записан, форма не ругается на модифицированность
Данные в базе изменились, а форма требует перечитать или конфликтует версий
FAQ
Можно ли обратиться к реквизиту формы из общего модуля?
Напрямую — нет, реквизиты существуют только в контексте конкретной формы. Из общего модуля работают с переданными параметрами: форма вызывает серверную функцию общего модуля и передаёт нужные значения. Передать саму форму параметром в серверный общий модуль тоже можно, но это усложняет архитектуру и редко оправдано.
Чем отличаются ДанныеФормыВЗначение и РеквизитФормыВЗначение?
Обе функции преобразуют данные формы в прикладной объект. РеквизитФормыВЗначение принимает имя реквизита строкой и работает с реквизитом верхнего уровня. ДанныеФормыВЗначение принимает само значение данных формы и удобна, когда объект уже получен как значение, например из коллекции. В типовых формах чаще используют РеквизитФормыВЗначение("Объект").
Почему на сервере не виден реквизит, добавленный программно?
Реквизиты, созданные через ИзменитьРеквизиты(), доступны на сервере так же, как созданные в конфигураторе, но только после выполнения этого метода. Если процедура с обращением вызывается до создания реквизита, будет ошибка. Также проверьте, что создание происходит в контекстной серверной процедуре — ИзменитьРеквизиты работает только на сервере.
Как на сервере обратиться к элементу формы, а не к реквизиту?
Через коллекцию Элементы: ЭтаФорма.Элементы.ИмяЭлемента. На сервере доступны не все свойства элементов — например, можно менять видимость, доступность, заголовок, но нельзя работать с фокусом ввода. Помните, что элемент формы и реквизит — разные сущности: элемент только отображает данные реквизита.
Что быстрее: контекстный или бесконтекстный серверный вызов?
Бесконтекстный вызов быстрее, потому что состояние формы не сериализуется и не передаётся на сервер. Разница заметна на «тяжёлых» формах с большими таблицами. Если процедуре нужны лишь пара параметров и запрос к базе — выносите её в &НаСервереБезКонтекста или в общий модуль.
Что важно запомнить
В серверных процедурах формы реквизиты доступны по имени, но основной реквизит — это ДанныеФормыСтруктура без методов объекта. Для вызова Записать() или Провести() используйте РеквизитФормыВЗначение с обратным преобразованием ЗначениеВРеквизитФормы. В бесконтекстных процедурах реквизиты недоступны — передавайте значения параметрами.