Как правильно заполнить реквизит типа ДанныеФормыКоллекция при активизации строки

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

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

В этой статье мы подробно разберем, как программно заполнить реквизит ДанныеФормыКоллекция, почему стандартное событие ПриАктивизацииСтроки требует осторожного обращения и какие архитектурные подходы считаются наиболее эффективными.

Проблема использования события ПриАктивизацииСтроки

Прежде чем переходить к коду, проанализируем ситуацию с событием ПриАктивизацииСтроки. Когда пользователь быстро прокручивает список номенклатуры, нажимая клавиши «вверх» или «вниз», это событие генерируется для каждой строки, через которую проходит курсор. Если в обработчике этого события прописан прямой вызов сервера, система будет пытаться выполнить десятки запросов в секунду.

Рассмотрим основные риски прямого вызова:

  1. «Тормоза» интерфейса: Каждое нажатие клавиши блокирует поток интерфейса до тех пор, пока сервер не вернет ответ. Пользователь ощущает «вязкость» управления.
  2. Зацикливание и стек: При контекстном вызове &НаСервере платформа может повторно активировать событие при обновлении данных формы, что в худшем случае приведет к переполнению стека.
  3. Нагрузка на сервер: Множество бесполезных запросов к базе данных (так как пользователя интересует только финальная строка, на которой он остановится).

Выясним причину, по которой конструктор формы часто не предлагает создать метод &НаСервере для этого события. Это осознанное ограничение платформы, подталкивающее разработчика к использованию неконтекстных вызовов или других механизмов оптимизации.

Решение 1. Использование обработчика ожидания

Чтобы избежать лишних серверных вызовов, мы применим механизм «дребезга» (debouncing). Суть его в том, чтобы подождать небольшую паузу (например, 0.2 секунды) после того, как пользователь нажал кнопку, и только если за это время курсор не переместился снова — идти на сервер.

Разберем реализацию по шагам:


&НаКлиенте
Процедура СписокНоменклатураПриАктивизацииСтроки(Элемент)
    // Отменяем предыдущий запланированный вызов, если пользователь нажал кнопку быстро
    ОтключитьОбработчикОжидания("ВыполнитьОбновлениеДанныхНаСервере");
    
    // Запускаем новый таймер. 0.2 секунды достаточно, чтобы пропустить быстрый скролл
    ПодключитьОбработчикОжидания("ВыполнитьОбновлениеДанныхНаСервере", 0.2, Истина);
КонецПроцедуры

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

Решение 2. Неконтекстный серверный вызов и КопироватьДанныеФормы

Теперь рассмотрим, как технически заполнить реквизит типа ДанныеФормыКоллекция, если мы не хотим тянуть на сервер контекст всей формы. Платформа предоставляет нам мощный инструмент — метод КопироватьДанныеФормы.

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


&НаКлиенте
Процедура ОбновитьОстаткиНаСервере(ВыбраннаяНоменклатура)
    // Подготавливаем временную переменную для коллекции
    ЛокальнаяКоллекция = ЭтаФорма.ТоварыВРезерве;
    
    // Вызываем неконтекстный серверный метод, передавая коллекцию как параметр
    ЗаполнитьРезервыНаСервереБезКонтекста(ВыбраннаяНоменклатура, ЛокальнаяКоллекция);
    
    // Копируем измененную структуру обратно в реквизит формы
    КопироватьДанныеФормы(ЛокальнаяКоллекция, ЭтаФорма.ТоварыВРезерве);
КонецПроцедуры

&НаСервереБезКонтекста
Процедура ЗаполнитьРезервыНаСервереБезКонтекста(Номенклатура, ДанныеКоллекции)
    // Преобразуем ДанныеФормыКоллекция в привычную ТаблицуЗначений
    ТЗ = ДанныеФормыВЗначение(ДанныеКоллекции, Тип("ТаблицаЗначений"));
    ТЗ.Очистить();
    
    // Выполняем запрос к базе данных
    Запрос = Новый Запрос;
    Запрос.Текст = 
    "ВЫБРАТЬ
    |   СвободныеОстатки.Склад,
    |   СвободныеОстатки.ВРезервеСоСкладаОстаток КАК Резерв
    |ИЗ
    |   РегистрНакопления.СвободныеОстатки.Остатки(, Номенклатура = &Номенклатура) КАК СвободныеОстатки";
    Запрос.УстановитьПараметр("Номенклатура", Номенклатура);
    
    Результат = Запрос.Выполнить();
    Если Не Результат.Пустой() Тогда
        ТЗ = Результат.Выгрузить();
    КонецЕсли;
    
    // Загружаем результат обратно в параметр ДанныеФормы
    ЗначениеВДанныеФормы(ТЗ, ДанныеКоллекции);
КонецПроцедуры

Важное примечание: Использование ДанныеФормыВЗначение и ЗначениеВДанныеФормы внутри метода &НаСервереБезКонтекста требует передачи коллекции в качестве параметра. Помните, что при передаче больших коллекций (тысячи строк) сериализация данных между клиентом и сервером может занять значительное время. В таких случаях иногда выгоднее использовать обычный контекстный вызов &НаСервере и метод коллекции Загрузить().

Решение 3. Динамический список как альтернатива

Если задача заключается только в отображении данных (без возможности ручного редактирования строк пользователем в подчиненной таблице), то использование ДанныеФормыКоллекция может быть избыточным. Посмотрим на ситуацию с точки зрения производительности: Динамический список (ДС) работает по принципу «ленивой загрузки» — он считывает только те данные, которые видны пользователю на экране.

Алгоритм работы с динамическим списком:

  1. Создаем на форме реквизит с типом ДинамическийСписок.
  2. В его произвольном запросе прописываем получение остатков/резервов с параметром &Номенклатура.
  3. В обработчике ожидания на клиенте просто устанавливаем параметр этого списка:

&НаКлиенте
Процедура ВыполнитьОбновлениеДанныхНаСервере()
    ТекущиеДанные = Элементы.СписокНоменклатура.ТекущиеДанные;
    Если ТекущиеДанные = Неопределено Тогда
        Возврат;
    КонецЕсли;

    // Установка параметра динамического списка не требует КопироватьДанныеФормы
    ЭтаФорма.СписокРезервов.Параметры.УстановитьЗначениеПараметра("Номенклатура", ТекущиеДанные.Номенклатура);
КонецПроцедуры

Преимущество этого метода в том, что платформа сама оптимизирует запросы, а интерфейс остается максимально отзывчивым даже при работе с большими объемами данных.

Решение 4. Построчное заполнение на клиенте

Если данных в подчиненной таблице совсем немного (например, 2-3 строки складов), мы можем использовать метод возврата массива структур. Это избавит нас от необходимости конвертировать всю таблицу в ТаблицуЗначений на сервере.

Посмотрим на пример реализации:


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

Этот метод хорош тем, что мы четко контролируем объем данных, передаваемых с сервера на клиент, и не используем тяжелые механизмы преобразования типов 1С.

Подведем итоги

Мы проанализировали несколько способов взаимодействия с коллекциями формы. Выбор конкретного метода зависит от ваших задач:

Соблюдение этих правил позволит создавать быстрые и надежные интерфейсы на платформе 1С:Предприятие 8.3.

← На главную