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