Мы часто сталкиваемся с ситуацией, когда, казалось бы, простой код для удаления строки из таблицы значений в 1С 8.3 не дает ожидаемого результата на форме. Строка программно удаляется, но визуально остается, или возникают ошибки при выполнении кода. Разберем эту проблему подробно и выясним, как ее корректно решить, используя лучшие практики платформы.
Давайте проанализируем исходный код автора и распространенные причины, по которым изменения в таблице значений могут не отображаться на форме или приводить к некорректной работе.
Для Каждого Стр Из Таблица Цикл
Док = стр.Ссылка1.ПолучитьОбъект();
// ...
Если Стат <> "Завершен" тогда
Таблица.Удалить(Стр);
КонецЕсли;
КонецЦикла;
В этом коде содержатся несколько потенциальных проблем:
Таблица циклом Для Каждого ... Из ... Цикл и одновременно пытаемся удалить из нее элементы. При удалении строки из коллекции Таблица ее внутренний порядок и количество элементов изменяются. Это может привести к тому, что:
Таблица.Количество()=1), последующие итерации могут быть некорректными.Таблица является реквизитом формы, необходимо явно сообщить форме об изменении.Док.Записать(). Это важное различие: если мы меняем табличную часть объекта (документа, справочника), то после изменений нужно записать сам объект. Если же мы работаем с таблицей значений, которая является *только* реквизитом формы, то Док.Записать() здесь не требуется, а нужно обновить сам реквизит формы.Ключевым моментом в работе с управляемыми формами является понимание взаимодействия между реквизитами формы и прикладными объектами (таблицами значений, табличными частями документов). Форма отображает данные, привязанные к ее реквизитам. Чтобы изменения, сделанные в прикладном объекте (например, в нашей ТаблицеЗначений), отобразились на форме, и наоборот, используются специальные функции.
Используем функцию РеквизитФормыВЗначение(). Она позволяет получить объект прикладного типа (например, ТаблицаЗначений) из реквизита формы. Это создает "снимок" или копию данных формы, с которой мы можем работать.
ТаблицаДанных = РеквизитФормыВЗначение("ИмяРеквизитаТаблицыНаФорме");
Здесь "ИмяРеквизитаТаблицыНаФорме" – это строковое имя реквизита формы (например, "МояТаблица" или "Таблица"), к которому привязано табличное поле на форме.
После того как мы внесли изменения в наш прикладной объект (ТаблицаДанных), нам нужно передать эти изменения обратно на форму, чтобы они стали видны пользователю. Для этого используем функцию ЗначениеВРеквизитФормы().
ЗначениеВРеквизитФормы(ТаблицаДанных, "ИмяРеквизитаТаблицыНаФорме");
Эта функция преобразует объект прикладного типа (вашу измененную таблицу значений) в заданный реквизит формы, что приводит к обновлению его отображения.
Теперь, когда мы понимаем механизм синхронизации данных между формой и прикладным объектом, рассмотрим наиболее надежные и эффективные методы удаления строк из объекта ТаблицаЗначений.
Это наиболее рекомендуемый подход, поскольку при удалении строки из коллекции ее индексы смещаются. Если мы будем идти от последнего элемента к первому, то удаление текущего элемента не повлияет на индексы еще не обработанных элементов. Так мы избежим пропуска строк и ошибок итерации.
Посмотрим на пример, адаптированный к исходной задаче автора:
&НаКлиенте
Процедура УдалитьСтрокиИзТаблицыНаФорме()
// 1. Получаем текущую таблицу значений с формы в прикладной объект
ТаблицаДанных = РеквизитФормыВЗначение("Таблица"); // "Таблица" - это имя вашего реквизита формы
// 2. Итерируем в обратном порядке и удаляем строки по условию
Для ИндексСтроки = ТаблицаДанных.Количество() - 1 По 0 Цикл
Стр = ТаблицаДанных[ИндексСтроки];
// Получаем объект документа для проверки статуса
// Эту часть лучше вынести на сервер, если Ссылка1 - это ссылка на объект БД
// Пример упрощен для демонстрации
Если ТипЗнч(Стр.Ссылка1) = Тип("ДокументСсылка.ИмяВашегоДокумента") Тогда // Проверка типа, чтобы избежать ошибки
ДокОбъект = Стр.Ссылка1.ПолучитьОбъект();
Если ДокОбъект.Статус <> Перечисления.СтатусыДокументов.Завершен Тогда // Предположим, есть Перечисление.СтатусыДокументов
ТаблицаДанных.Удалить(ИндексСтроки); // Удаляем строку по индексу
КонецЕсли;
Иначе
// Возможно, нужно удалить строки, где Ссылка1 некорректна
ТаблицаДанных.Удалить(ИндексСтроки);
КонецЕсли;
КонецЦикла;
// 3. Передаем измененную таблицу значений обратно на форму
ЗначениеВРеквизитФормы(ТаблицаДанных, "Таблица");
// Опционально: можно явно обновить элементы формы
// ЭтаФорма.ОбновитьОтображениеДанных();
// Элементы.ВашаТаблицаНаФорме.Обновить(); // Если нужно обновить только одно табличное поле
КонецПроцедуры
В этом примере Таблица.Удалить(ИндексСтроки); является наиболее универсальным способом удаления, но можно также использовать Таблица.Удалить(Стр);, если Стр является строкой из текущей таблицы.
Этот метод полезен, когда логика фильтрации сложна, нужно удалить большое количество строк или если мы хотим быть абсолютно уверены в корректности итоговой таблицы. Мы создаем новую пустую таблицу значений, определяем для нее те же колонки, что и у исходной, а затем копируем в нее только те строки из исходной таблицы, которые хотим оставить.
Рассмотрим пошаговую реализацию:
&НаКлиенте
Процедура ФильтроватьТаблицуНаФорме()
// 1. Получаем текущую таблицу значений с формы
ТаблицаИсходная = РеквизитФормыВЗначение("Таблица"); // "Таблица" - имя реквизита формы
// 2. Создаем новую пустую таблицу значений
НоваяТаблица = Новый ТаблицаЗначений;
// 3. Скопируем колонки из исходной таблицы в новую
Для Каждого Колонка Из ТаблицаИсходная.Колонки Цикл
НоваяТаблица.Колонки.Добавить(Колонка.Имя, Колонка.ТипЗначения, Колонка.Заголовок, Колонка.Ширина);
КонецЦикла;
// 4. Перебираем исходную таблицу и добавляем в новую только те строки, которые хотим оставить
Для Каждого Стр Из ТаблицаИсходная Цикл
// Ваша логика: если строку нужно оставить
// Например, если Статус = "Завершен"
// Получаем объект документа для проверки статуса
// Эту часть лучше вынести на сервер, если Ссылка1 - это ссылка на объект БД
Если ТипЗнч(Стр.Ссылка1) = Тип("ДокументСсылка.ИмяВашегоДокумента") Тогда
ДокОбъект = Стр.Ссылка1.ПолучитьОбъект();
Если ДокОбъект.Статус = Перечисления.СтатусыДокументов.Завершен Тогда // Пример условия: оставляем завершенные
НоваяСтрока = НоваяТаблица.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, Стр); // Копируем все данные строки
КонецЕсли;
Иначе
// Возможно, оставляем строки с некорректной ссылкой, если это часть логики
// НоваяСтрока = НоваяТаблица.Добавить();
// ЗаполнитьЗначенияСвойств(НоваяСтрока, Стр);
КонецЕсли;
КонецЦикла;
// 5. Присваиваем новую, отфильтрованную таблицу обратно реквизиту формы
ЗначениеВРеквизитФормы(НоваяТаблица, "Таблица");
// Обновление формы произойдет автоматически после ЗначениеВРеквизитФормы,
// но можно добавить явное обновление при необходимости:
// ЭтаФорма.ОбновитьОтображениеДанных();
КонецПроцедуры
НайтиСтроки() для получения массива строк для удаления.
Если вам нужно удалить строки по определенному условию, метод НайтиСтроки() может быть удобен. Он возвращает массив ссылок на строки, удовлетворяющих заданному отбору. Затем эти строки можно удалить из исходной таблицы, перебирая полученный массив.
&НаКлиенте
Процедура УдалитьСтрокиЧерезНайтиСтроки()
// 1. Получаем текущую таблицу значений с формы
ТаблицаДанных = РеквизитФормыВЗначение("Таблица");
// 2. Определяем структуру отбора для поиска строк, которые нужно удалить
// Предполагаем, что у ТаблицаДанных есть колонка "Статус"
СтруктураОтбора = Новый Структура("Статус", "НеЗавершен"); // Пример отбора для удаления
// Получаем массив ссылок на строки, которые соответствуют условию удаления
// Важно: НайтиСтроки() работает с текущими значениями таблицы,
// но возвращает массив, который не изменяется при удалении из основной таблицы
МассивСтрокДляУдаления = ТаблицаДанных.НайтиСтроки(СтруктураОтбора);
// 3. Перебираем полученный массив и удаляем каждую найденную строку из исходной таблицы
Для Каждого СтрУдалить Из МассивСтрокДляУдаления Цикл
ТаблицаДанных.Удалить(СтрУдалить);
КонецЦикла;
// 4. Передаем измененную таблицу значений обратно на форму
ЗначениеВРеквизитФормы(ТаблицаДанных, "Таблица");
КонецПроцедуры
Важное примечание: В этом случае МассивСтрокДляУдаления является копией ссылок на строки, а не изменяющейся коллекцией. Поэтому удаление строк из ТаблицаДанных не нарушает итерацию по МассивСтрокДляУдаления, и этот метод является безопасным для использования с циклом Для Каждого.
Как мы уже выяснили, одного изменения объекта ТаблицаЗначений недостаточно для автоматического обновления формы. Помимо передачи измененной таблицы на форму с помощью ЗначениеВРеквизитФормы(), существуют и другие методы для обновления визуального представления:
ЭтаФорма.ОбновитьОтображениеДанных();
Этот метод приводит к обновлению всех элементов управляемой формы, привязанных к данным. Это более общий способ, который может быть полезен, если изменения затрагивают несколько элементов или если вы хотите убедиться, что все связанные данные на форме синхронизированы.
Элементы.ИмяТабличногоПоля.Обновить() или Элементы.ИмяТабличногоПоля.ОбновитьСтроки();
Если изменения касаются только конкретного табличного поля, можно вызвать метод Обновить() или ОбновитьСтроки() у соответствующего элемента формы. Это более локализованное обновление, которое может быть предпочтительнее для оптимизации производительности, если форма сложная и содержит много элементов.
Пример:
Элементы.МояТаблицаНаФорме.Обновить(); // "МояТаблицаНаФорме" - имя элемента формы
В процессе работы с таблицами значений и формами могут возникнуть типичные ошибки. Давайте рассмотрим их и способы их устранения.
Эта ошибка (как в сообщении 8) означает, что переменная, к которой вы применяете метод Удалить(), не является объектом типа ТаблицаЗначений или СтрокаТаблицыЗначений. Убедитесь, что вы вызываете Удалить() у объекта ТаблицаЗначений и передаете ему либо объект СтрокаТаблицыЗначений, либо числовой индекс удаляемой строки.
тз — это строка таблицы, а не сама таблица:
тз.Удалить();
Удалить() у объекта ТаблицаЗначений:
Таблица.Удалить(СтрокаТаблицы);
// или
Таблица.Удалить(ИндексСтроки);
Как верно было отмечено в сообщении 10, если ваша таблица значений — это просто реквизит формы, не являющийся табличной частью документа, то вызывать Док.Записать() не нужно. Этот метод используется для сохранения изменений в объектах базы данных (документах, справочниках). Если же вы изменяете табличную часть документа, то после манипуляций с ней, естественно, необходимо получить объект документа (РеквизитФормыВЗначение("Объект")), внести изменения в его табличную часть, а затем записать сам документ (Объект.Записать()) и вернуть его на форму (ЗначениеВРеквизитФормы(Объект, "Объект")).
РеквизитФормыВЗначение / ЗначениеВРеквизитФормы.
Убедитесь, что вы используете правильные названия функций, как было подмечено в сообщении 7. Частая ошибка – опечатка или перепутывание порядка слов в названии функции.
РеквизитФормыВЗначение("ИмяРеквизита") и ЗначениеВРеквизитФормы(Значение, "ИмяРеквизита").РеквизитВЗначениеФормы("ИмяРеквизита").Использование приведенных подходов позволит вам корректно управлять данными в таблицах значений на управляемых формах 1С 8.3, обеспечивая как программное изменение данных, так и их адекватное визуальное обновление для пользователя. Всегда помните о контексте работы (форма vs. объект) и выбирайте наиболее подходящий метод удаления строк.