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