Как получить данные с сервера в общем клиентском модуле 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке в среде «1С:Предприятие 8.3» на управляемых формах разработчики часто сталкиваются с необходимостью выноса повторяющегося кода в общие модули. Одной из самых распространенных задач является создание общих процедур для работы с диалогами, которые вызываются из множества разных форм. Однако здесь возникает техническое препятствие: диалоги — это чисто клиентская сущность, а данные для условий в этих диалогах (например, проверка владельца справочника или кода объекта) требуют обращения к серверу. Рассмотрим подробно, как правильно организовать архитектуру взаимодействия клиентского общего модуля с серверными данными, чтобы это было быстро, удобно и соответствовало стандартам разработки.

Разберем суть проблемы: почему «точка» не работает?

Представим ситуацию: вы создали общий модуль с установленным флагом Клиент (управляемое приложение). В этом модуле вы описываете логику открытия вопроса пользователю. В зависимости от ответа, вам нужно проверить какое-либо свойство ссылки. Программисты-новички часто пытаются написать код так:


// В общем клиентском модуле
Процедура ПроверитьИПоказатьЗначение(Ссылка) Экспорт
    Если ЗначениеЗаполнено(Ссылка.Владелец) Тогда // ОШИБКА!
        ПоказатьЗначение(, Ссылка.Владелец);
    Иначе
        ПоказатьЗначение(, Ссылка);
    КонецЕсли;
КонецПроцедуры

Проанализируем этот код. В управляемом приложении обращение через точку от ссылки (Ссылка.Владелец) на стороне клиента вызывает неявный и крайне неэффективный серверный вызов. Платформа «под капотом» обращается к серверу, чтобы получить весь объект, что замедляет работу интерфейса. Более того, если в общем модуле стоит только флаг Клиент, система может просто «не увидеть» серверные методы или контекст данных. Выясним, как решить эту задачу правильно.

Решение 1: Создание парных модулей (Архитектурный подход)

Самый правильный и надежный способ, рекомендуемый фирмой «1С», — это разделение логики на два модуля. Рассмотрим эту схему подробнее. Мы создаем два общих модуля:

  1. ОбщийМодульКлиент — здесь хранятся только клиентские процедуры (диалоги, работа с формой). Флаг: Клиент (управляемое приложение).
  2. ОбщийМодульВызовСервера — здесь хранятся серверные «функции-прослойки». Флаги: Сервер и Вызов сервера.

Важно помнить, что флаг Вызов сервера доступен для активации только тогда, когда установлен флаг Сервер. Именно эта комбинация позволяет вызывать функции модуля прямо из клиентского кода. Посмотрим на пример реализации.

В серверном модуле ОбщийМодульВызовСервера напишем функцию:


Функция ПолучитьДанныеДляДиалога(Ссылка) Экспорт
    СтруктураДанных = Новый Структура;
    СтруктураДанных.Вставить("Владелец", Ссылка.Владелец);
    СтруктураДанных.Вставить("ЭтоЗаполнено", ЗначениеЗаполнено(Ссылка.Владелец));
    Возврат СтруктураДанных;
КонецФункции

А в клиентском модуле ОбщийМодульКлиент вызовем её:


Процедура ПроверитьИПоказатьЗначение(Ссылка) Экспорт
    // Делаем ОДИН явный вызов сервера
    Данные = ОбщийМодульВызовСервера.ПолучитьДанныеДляДиалога(Ссылка);
    
    Если Данные.ЭтоЗаполнено Тогда
        ПоказатьЗначение(, Данные.Владелец);
    Иначе
        ПоказатьЗначение(, Ссылка);
    КонецЕсли;
КонецПроцедуры

Этот подход позволяет четко разграничить ответственность уровней системы и минимизировать количество пересылаемых данных между клиентом и сервером.

Решение 2: Использование функций Библиотеки стандартных подсистем (БСП)

Если в вашей конфигурации используется БСП (что справедливо для большинства современных типовых решений), вам не обязательно создавать свои велосипеды. Разберем, как воспользоваться готовыми инструментами. В БСП есть модуль ОбщегоНазначения, который уже имеет флаг Вызов сервера.

Для получения одного реквизита используем метод ЗначениеРеквизитаОбъекта. Проанализируем пример кода:


// На клиенте
Владелец = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Ссылка, "Владелец");
Если ЗначениеЗаполнено(Владелец) Тогда
    ПоказатьЗначение(, Владелец);
Иначе
    ПоказатьЗначение(, Ссылка);
КонецЕсли;

Если же нам нужно получить сразу несколько полей (например, Код, Наименование и Владелец), лучше использовать метод ЗначенияРеквизитовОбъекта. Это позволит получить все данные за один серверный вызов, что критически важно для производительности в условиях медленного интернета.


// Получаем структуру реквизитов
Реквизиты = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(Ссылка, "Владелец, Код, Наименование");

// Обращаемся к данным без вызова сервера
Если ЗначениеЗаполнено(Реквизиты.Владелец) Тогда
    // Логика работы
КонецЕсли;

Решение 3: Оптимизация при массовой работе (Массивы ссылок)

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


МассивСсылок = Новый Массив;
// Заполняем массив ссылками...

// Один вызов сервера для всех объектов сразу!
СоответствиеДанных = ОбщегоНазначения.ЗначенияРеквизитовОбъектов(МассивСсылок, "Владелец, Код");

Для Каждого СсылкаИзСписка Из МассивСсылок Цикл
    ДанныеОбъекта = СоответствиеДанных[СсылкаИзСписка];
    Если ДанныеОбъекта <> Неопределено Тогда
        // Работаем с данными
    КонецЕсли;
КонецЦикла;

Важные ограничения и стандарты

Проанализируем несколько критических моментов, которые нельзя игнорировать при переносе кода в общие модули:

Таким образом, для разруливания ситуации с диалогами в общем модуле, мы рекомендуем использовать архитектурную связку из двух модулей: клиентского для интерфейса и серверного (с галкой Вызов сервера) для получения данных. Это обеспечит чистоту кода и высокую скорость работы приложения.

← На главную