Как реализовать связь между двумя табличными частями и сохранять данные в пределах формы 1С

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

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

Проанализируем ситуацию и разберем несколько подходов к решению этой задачи: от стандартных механизмов платформы до более гибких программных методов.

Метод 1: Связь через уникальный идентификатор (Ключ связи)

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

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

  1. Подготовка структур данных: В основную таблицу (ТЧ1) и в подчиненную таблицу (ТЧ2) добавим колонку КлючСвязи. Тип этой колонки лучше всего выбрать УникальныйИдентификатор или Число.
  2. Генерация ключа: При добавлении строки в ТЧ1 программно генерируем новый ключ. Если используем число — берем максимальный индекс, если UUID — используем функцию Новый УникальныйИдентификатор().
  3. Фильтрация на форме: Используем свойство элемента формы «Таблица» под названием ОтборСтрок.

Посмотрим на пример кода, который необходимо разместить в обработчике события ПриАктивизацииСтроки основной таблицы:


&НаКлиенте
Процедура ТЧ1ПриАктивизацииСтроки(Элемент)
    ТекущиеДанные = Элементы.ТЧ1.ТекущиеДанные;
    Если ТекущиеДанные = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    // Устанавливаем отбор в подчиненной таблице по ключу текущей строки
    Элементы.ТЧ2.ОтборСтрок = Новый ФиксированнаяСтруктура("КлючСвязи", ТекущиеДанные.КлючСвязи);
КонецПроцедуры

При добавлении новой строки в подчиненную таблицу (ТЧ2) нам также нужно гарантировать, что она «привяжется» к основной строке:


&НаКлиенте
Процедура ТЧ2ПередНачаломДобавления(Элемент, Отказ, Копирование, Родитель, Группа, Параметр)
    ТекущиеДанныеТЧ1 = Элементы.ТЧ1.ТекущиеДанные;
    Если ТекущиеДанныеТЧ1 <> Неопределено Тогда
        // Автоматически подставляем ключ из основной таблицы
        Элементы.ТЧ2.ТекущиеДанные.КлючСвязи = ТекущиеДанныеТЧ1.КлючСвязи;
    КонецЕсли;
КонецПроцедуры

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

Метод 2: Хранение данных в структурах (Тип «Произвольный»)

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

Проанализируем этот подход. В ТЧ1 создается реквизит с типом Произвольный. При деактивации строки (или перед сменой строки) данные из ТЧ2 выгружаются в этот реквизит, а при активации новой строки — загружаются обратно.

Рассмотрим пример кода для сохранения и восстановления данных:


&НаСервере
Процедура СохранитьТЧ2ВСтрокуТЧ1(ИндексСтрокиТЧ1)
    СтрокаТЧ1 = Объект.ТЧ1[ИндексСтрокиТЧ1];
    // Выгружаем ТЧ2 в таблицу значений, затем преобразуем в массив структур
    ТЗ = Объект.ТЧ2.Выгрузить();
    МассивДанных = Новый Массив;
    Для Каждого СтрТЗ Из ТЗ Цикл
        СтруктураСтроки = Новый Структура("Номенклатура, Количество, Цена");
        ЗаполнитьЗначенияСвойств(СтруктураСтроки, СтрТЗ);
        МассивДанных.Добавить(СтруктураСтроки);
    КонецЦикла;
    СтрокаТЧ1.ДанныеХранения = МассивДанных;
КонецПроцедуры

&НаСервере
Процедура ВосстановитьТЧ2ИзСтрокиТЧ1(ТекущаяСтрока)
    Объект.ТЧ2.Очистить();
    Если ТипЗнч(ТекущаяСтрока.ДанныеХранения) = Тип("Массив") Тогда
        Для Каждого СтруктураИзХранилища Из ТекущаяСтрока.ДанныеХранения Цикл
            НоваяСтр = Объект.ТЧ2.Добавить();
            ЗаполнитьЗначенияСвойств(НоваяСтр, СтруктураИзХранилища);
        КонецЦикла;
    КонецЕсли;
КонецПроцедуры

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

Метод 3: Использование временного хранилища

Выясним, как оптимизировать передачу данных, если подчиненные таблицы очень объемные. Вместо того чтобы хранить данные непосредственно в строке таблицы, мы можем поместить их во ВременноеХранилище, а в строке ТЧ1 хранить только адрес (строку).

Разберем алгоритм работы:

  1. При изменении данных в ТЧ2 мы вызываем серверный метод.
  2. На сервере выгружаем ТЧ2 в ТаблицуЗначений.
  3. Помещаем её в хранилище с помощью ПоместитьВоВременноеХранилище(ТЗ, УникальныйИдентификаторФормы).
  4. Записываем полученный адрес в колонку АдресДанных текущей строки ТЧ1.

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

Оптимизация события ПриАктивизацииСтроки

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

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


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

&НаКлиенте
Процедура ВыполнитьОбновлениеПодчиненнойТаблицы()
    // Здесь вызываем логику фильтрации или загрузки данных
    ОбновитьОтображениеТЧ2();
КонецПроцедуры

Итоги и рекомендации

Выбор метода зависит от ваших задач:

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

← На главную