При разработке в среде «1С:Предприятие 8.3» на управляемых формах разработчики часто сталкиваются с необходимостью выноса повторяющегося кода в общие модули. Одной из самых распространенных задач является создание общих процедур для работы с диалогами, которые вызываются из множества разных форм. Однако здесь возникает техническое препятствие: диалоги — это чисто клиентская сущность, а данные для условий в этих диалогах (например, проверка владельца справочника или кода объекта) требуют обращения к серверу. Рассмотрим подробно, как правильно организовать архитектуру взаимодействия клиентского общего модуля с серверными данными, чтобы это было быстро, удобно и соответствовало стандартам разработки.
Представим ситуацию: вы создали общий модуль с установленным флагом Клиент (управляемое приложение). В этом модуле вы описываете логику открытия вопроса пользователю. В зависимости от ответа, вам нужно проверить какое-либо свойство ссылки. Программисты-новички часто пытаются написать код так:
// В общем клиентском модуле
Процедура ПроверитьИПоказатьЗначение(Ссылка) Экспорт
Если ЗначениеЗаполнено(Ссылка.Владелец) Тогда // ОШИБКА!
ПоказатьЗначение(, Ссылка.Владелец);
Иначе
ПоказатьЗначение(, Ссылка);
КонецЕсли;
КонецПроцедуры
Проанализируем этот код. В управляемом приложении обращение через точку от ссылки (Ссылка.Владелец) на стороне клиента вызывает неявный и крайне неэффективный серверный вызов. Платформа «под капотом» обращается к серверу, чтобы получить весь объект, что замедляет работу интерфейса. Более того, если в общем модуле стоит только флаг Клиент, система может просто «не увидеть» серверные методы или контекст данных. Выясним, как решить эту задачу правильно.
Самый правильный и надежный способ, рекомендуемый фирмой «1С», — это разделение логики на два модуля. Рассмотрим эту схему подробнее. Мы создаем два общих модуля:
Важно помнить, что флаг Вызов сервера доступен для активации только тогда, когда установлен флаг Сервер. Именно эта комбинация позволяет вызывать функции модуля прямо из клиентского кода. Посмотрим на пример реализации.
В серверном модуле ОбщийМодульВызовСервера напишем функцию:
Функция ПолучитьДанныеДляДиалога(Ссылка) Экспорт
СтруктураДанных = Новый Структура;
СтруктураДанных.Вставить("Владелец", Ссылка.Владелец);
СтруктураДанных.Вставить("ЭтоЗаполнено", ЗначениеЗаполнено(Ссылка.Владелец));
Возврат СтруктураДанных;
КонецФункции
А в клиентском модуле ОбщийМодульКлиент вызовем её:
Процедура ПроверитьИПоказатьЗначение(Ссылка) Экспорт
// Делаем ОДИН явный вызов сервера
Данные = ОбщийМодульВызовСервера.ПолучитьДанныеДляДиалога(Ссылка);
Если Данные.ЭтоЗаполнено Тогда
ПоказатьЗначение(, Данные.Владелец);
Иначе
ПоказатьЗначение(, Ссылка);
КонецЕсли;
КонецПроцедуры
Этот подход позволяет четко разграничить ответственность уровней системы и минимизировать количество пересылаемых данных между клиентом и сервером.
Если в вашей конфигурации используется БСП (что справедливо для большинства современных типовых решений), вам не обязательно создавать свои велосипеды. Разберем, как воспользоваться готовыми инструментами. В БСП есть модуль ОбщегоНазначения, который уже имеет флаг Вызов сервера.
Для получения одного реквизита используем метод ЗначениеРеквизитаОбъекта. Проанализируем пример кода:
// На клиенте
Владелец = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Ссылка, "Владелец");
Если ЗначениеЗаполнено(Владелец) Тогда
ПоказатьЗначение(, Владелец);
Иначе
ПоказатьЗначение(, Ссылка);
КонецЕсли;
Если же нам нужно получить сразу несколько полей (например, Код, Наименование и Владелец), лучше использовать метод ЗначенияРеквизитовОбъекта. Это позволит получить все данные за один серверный вызов, что критически важно для производительности в условиях медленного интернета.
// Получаем структуру реквизитов
Реквизиты = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(Ссылка, "Владелец, Код, Наименование");
// Обращаемся к данным без вызова сервера
Если ЗначениеЗаполнено(Реквизиты.Владелец) Тогда
// Логика работы
КонецЕсли;
Рассмотрим ситуацию, когда нам нужно обработать не одну ссылку, а целый список. Постоянно бегать на сервер за каждым объектом в цикле — плохая практика. В БСП для этого предусмотрена функция ЗначенияРеквизитовОбъектов (во множественном числе). Посмотрим на пример работы с массивом:
МассивСсылок = Новый Массив;
// Заполняем массив ссылками...
// Один вызов сервера для всех объектов сразу!
СоответствиеДанных = ОбщегоНазначения.ЗначенияРеквизитовОбъектов(МассивСсылок, "Владелец, Код");
Для Каждого СсылкаИзСписка Из МассивСсылок Цикл
ДанныеОбъекта = СоответствиеДанных[СсылкаИзСписка];
Если ДанныеОбъекта <> Неопределено Тогда
// Работаем с данными
КонецЕсли;
КонецЦикла;
Проанализируем несколько критических моментов, которые нельзя игнорировать при переносе кода в общие модули:
СправочникОбъект, ДокументОбъект или ТаблицаЗначений. Платформа выдаст ошибку. Передавайте только ссылки, структуры, массивы или примитивные типы (строки, числа).ВызовСервера в названии (например, РаботаСДиалогамиВызовСервера). Это помогает другим разработчикам сразу понять, что вызов процедур из этого модуля является «дорогим» (серверным).Таким образом, для разруливания ситуации с диалогами в общем модуле, мы рекомендуем использовать архитектурную связку из двух модулей: клиентского для интерфейса и серверного (с галкой Вызов сервера) для получения данных. Это обеспечит чистоту кода и высокую скорость работы приложения.