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