Как программно итерироваться по строкам табличной части на форме и выполнять код, зависящий от текущей строки?

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

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

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

Подход 1: Программное изменение текущей строки через ее идентификатор

Этот метод наиболее близок к имитации действий пользователя. Мы будем итерироваться по строкам табличной части и для каждой строки явно устанавливать ее в качестве текущей для элемента формы. После этого мы можем вызывать типовой код, который обращается к Элементы.ИмяТабличнойЧасти.ТекущиеДанные.

Шаги реализации:

  1. Получаем идентификатор строки: Каждая строка табличной части имеет уникальный идентификатор, который можно получить с помощью метода ПолучитьИдентификатор(). Этот идентификатор используется для установки свойства ТекущаяСтрока элемента формы.
  2. Итерируемся по строкам: Используем цикл Для Каждого по данным табличной части объекта (например, Объект.Товары).
  3. Устанавливаем текущую строку формы: Внутри цикла присваиваем полученный идентификатор свойству Элементы.ИмяТабличнойЧасти.ТекущаяСтрока.
  4. Вызываем типовой код: После установки текущей строки вызываем нашу процедуру или типовой обработчик.

Давайте рассмотрим подробнее этот подход на примере, основанном на сообщении 2 и 4 из обсуждения:

Предположим, у нас есть типовая процедура, которая выглядит примерно так (как в сообщении 4):


&НаКлиенте
Процедура ТоварыСтавкаНДСПриИзменении(Элемент)
    // Эта процедура ожидает, что текущая строка уже установлена
    ТекущаяСтрока = Элементы.Товары.ТекущиеДанные;
    СтруктураДействий = Новый Структура;
    СтруктураДействий.Вставить("ПересчитатьСуммуНДС", ОбработкаТабличнойЧастиТоварыКлиент.СтруктураПересчетаСуммыНДСВСтрокеТЧ(Объект));
    СтруктураДействий.Вставить("ПересчитатьСумму");
    СтруктураДействий.Вставить("ПересчитатьСуммуСУчетомРучнойСкидки", Новый Структура("Очищать", Ложь));
    СтруктураДействий.Вставить("ПересчитатьСуммуСУчетомАвтоматическойСкидки", Новый Структура("Очищать", Истина));
    ОбработкаТабличнойЧастиТоварыКлиент.ПриИзмененииРеквизитовВТЧКлиент(Объект.Товары, ТекущаяСтрока, СтруктураДействий, КэшированныеЗначения);
    ОбработкаТабличнойЧастиТоварыКлиент.ВыделитьАгентскоеВознаграждение(Объект, ЭтотОбъект, СтруктураДействий, ТекущаяСтрока, КэшированныеЗначения);
    ОбновитьИтоговыеПоказателиКлиентСервер(ЭтотОбъект);
КонецПроцедуры

Чтобы запустить эту логику для каждой строки, мы можем создать вспомогательную процедуру, которая будет устанавливать текущую строку, а затем вызывать необходимую логику. Как было показано в сообщении 2, это можно сделать следующим образом:


&НаКлиенте
Процедура ТоварыСтавкаНДСПриИзмененииКастом(СтрокаТЗ)

    // 1. Получаем идентификатор строки, по которой хотим работать
    ИдентификаторСтроки = СтрокаТЗ.ПолучитьИдентификатор();

    // 2. Устанавливаем эту строку как текущую для элемента формы
    // Это важно, так как типовой код может обращаться к Элементы.Товары.ТекущиеДанные
    Элементы.Товары.ТекущаяСтрока = ИдентификаторСтроки;

    // 3. Получаем текущие данные уже из элемента формы,
    // чтобы убедиться, что они соответствуют только что установленной строке.
    // Хотя в данном случае ТекущаяСтрока (переданная как СтрокаТЗ)
    // и Элементы.Товары.ТекущиеДанные будут указывать на один и тот же объект,
    // такой подход гарантирует соответствие ожиданиям типового кода.
    ТекущаяСтрокаОбработки = Элементы.Товары.ТекущиеДанные;

    СтруктураДействий = Новый Структура;
    СтруктураДействий.Вставить("ПересчитатьСуммуНДС", ОбработкаТабличнойЧастиТоварыКлиент.СтруктураПересчетаСуммыНДСВСтрокеТЧ(Объект));
    СтруктураДействий.Вставить("ПересчитатьСумму");
    СтруктураДействий.Вставить("ПересчитатьСуммуСУчетомРучнойСкидки", Новый Структура("Очищать", Ложь));
    СтруктураДействий.Вставить("ПересчитатьСуммуСУчетомАвтоматическойСкидки", Новый Структура("Очищать", Истина));

    // Вызываем типовой код, передавая данные, полученные через ТекущиеДанные формы
    ОбработкаТабличнойЧастиТоварыКлиент.ПриИзмененииРеквизитовВТЧКлиент(Объект.Товары, ТекущаяСтрокаОбработки, СтруктураДействий, КэшированныеЗначения);
    ОбработкаТабличнойЧастиТоварыКлиент.ВыделитьАгентскоеВознаграждение(Объект, ЭтотОбъект, СтруктураДействий, ТекущаяСтрокаОбработки, КэшированныеЗначения);
    ОбновитьИтоговыеПоказателиКлиентСервер(ЭтотОбъект);

КонецПроцедуры

Теперь, для итерации по всем строкам, мы можем использовать цикл, который будет вызывать эту "кастомную" процедуру для каждой строки:


&НаКлиенте
Процедура ПересчитатьВсеСтрокиПоНДС()
    Для Каждого строкаТЗ Из Объект.Товары Цикл
        ТоварыСтавкаНДСПриИзмененииКастом(строкаТЗ);
    КонецЦикла;

    // После завершения цикла обязательно обновите элемент формы,
    // чтобы все изменения отобразились визуально.
    Элементы.Товары.Обновить();
КонецПроцедуры

Преимущества этого подхода:

Недостатки:

Подход 2: Адаптация типового кода для прямой обработки строки

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

Шаги реализации:

  1. Создаем новую процедуру (или модифицируем существующую): Эта процедура будет принимать в качестве параметра непосредственно объект-строку табличной части (например, ТекущаяСтрока).
  2. Исключаем получение текущих данных из формы: Убираем строки типа ТекущаяСтрока = Элементы.Товары.ТекущиеДанные; из новой процедуры, так как строка уже передана как параметр.
  3. Вызываем новую процедуру в цикле: Итерируемся по Объект.Товары и вызываем нашу новую процедуру, передавая ей каждую строку.

Давайте посмотрим на пример, основанный на сообщении 5:

Сначала мы создаем или адаптируем процедуру, которая будет работать с переданной строкой напрямую. Назовем ее ТоварыСтавкаНДСКастом, как было предложено:


&НаКлиенте
Процедура ТоварыСтавкаНДСКастом(ТекущаяСтрока) // Теперь строка передается как параметр

    // Убраны строки, устанавливающие ТекущаяСтрока формы и получающие ТекущиеДанные,
    // так как мы работаем непосредственно с переданной строкой.

    СтруктураДействий = Новый Структура;
    СтруктураДействий.Вставить("ПересчитатьСуммуНДС", ОбработкаТабличнойЧастиТоварыКлиент.СтруктураПересчетаСуммыНДСВСтрокеТЧ(Объект));
    СтруктураДействий.Вставить("ПересчитатьСумму");
    СтруктураДействий.Вставить("ПересчитатьСуммуСУчетомРучнойСкидки", Новый Структура("Очищать", Ложь));
    СтруктураДействий.Вставить("ПересчитатьСуммуСУчетомАвтоматическойСкидки", Новый Структура("Очищать", Истина));

    ОбработкаТабличнойЧастиТоварыКлиент.ПриИзмененииРеквизитовВТЧКлиент(Объект.Товары, ТекущаяСтрока, СтруктураДействий, КэшированныеЗначения);
    ОбработкаТабличнойЧастиТоварыКлиент.ВыделитьАгентскоеВознаграждение(Объект, ЭтотОбъект, СтруктураДействий, ТекущаяСтрока, КэшированныеЗначения);
    ОбновитьИтоговыеПоказателиКлиентСервер(ЭтотОбъект);

КонецПроцедуры

Затем, для итерации по всем строкам, мы просто вызываем эту новую процедуру в цикле, передавая ей каждую строку:


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

    // В данном случае, если изменения затрагивают только данные объекта,
    // а не визуальное представление, обновление формы может быть выполнено один раз в конце.
    Элементы.Товары.Обновить();
КонецПроцедуры

Преимущества этого подхода:

Недостатки:

Когда и какой подход использовать?

Обновление визуального представления формы

Вне зависимости от выбранного подхода, после того как вы программно внесли изменения в данные табличной части (например, изменили значения в строках), часто требуется обновить визуальное представление на форме. Для этого используется метод Обновить() элемента формы табличной части:

Элементы.ИмяТабличнойЧасти.Обновить();

Этот метод заставит элемент формы перечитать данные из источника (например, из Объект.Товары) и отобразить актуальное состояние. Рекомендуем вызывать его один раз после завершения всего цикла итерации, а не в каждой итерации, чтобы избежать лишней перерисовки и повысить производительность, как было отмечено в сообщениях 7 и 10.

Таким образом, мы видим, что существуют эффективные способы для программной работы с табличными частями на форме, позволяющие запускать типовую логику для всех строк, учитывая при этом особенности архитектуры 1С:Предприятия.

← На главную