В процессе разработки конфигураций на платформе 1С:Предприятие мы часто сталкиваемся с необходимостью отображения и обновления данных, особенно когда речь идет о табличных частях реквизитов. Нередко возникает ситуация, когда табличная часть одного объекта (например, справочника) программно изменяется из другого объекта, и нам требуется, чтобы эти изменения мгновенно или своевременно отразились на открытой форме.
Давайте вместе разберем эту непростую, но очень распространенную задачу. Мы проанализируем, что такое "табличная часть реквизита", выясним основные методы для её обновления на форме и рассмотрим различные сценарии применения этих методов.
Что такое "табличная часть реквизита"?
Прежде чем углубляться в методы обновления, давайте уточним, что мы подразумеваем под "табличной частью реквизита". В 1С:Предприятии это структурированная таблица, которая является частью элемента объекта (например, справочника, документа или плана видов характеристик). В отличие от обычных реквизитов, хранящих одно значение, табличная часть может содержать множество строк данных, каждая из которых, в свою очередь, имеет несколько колонок. Например, у справочника "Номенклатура" может быть табличная часть "Характеристики" для хранения информации о размере, цвете, материале и прочих свойствах товара. Эта табличная часть настраивается в конфигураторе и по сути является единым целым с объектом-владельцем, считываясь и записываясь вместе с ним.
Основная проблема возникает, когда мы изменяем такую табличную часть программно (например, из другой формы или фонового задания), и форма, на которой отображается измененная табличная часть, не "знает" об этих изменениях и не обновляет свои данные автоматически.
ОповеститьОбИзменении()Один из наиболее часто используемых и эффективных способов уведомления форм об изменениях объекта — это метод ОповеститьОбИзменении(). Давайте рассмотрим его подробнее.
Как работает ОповеститьОбИзменении()?
При вызове метода ОповеститьОбИзменении() в него передается ссылка на объект (или ключ записи), об изменении которого необходимо оповестить. После этого платформа 1С:Предприятие автоматически уведомляет все открытые на клиенте формы, которые содержат динамические списки, основанные на основной таблице измененного объекта. Эти динамические списки затем обновляют свои данные, отражая последние изменения.
Преимущество этого подхода заключается в его простоте: нам не нужно знать, какие именно формы открыты и где отображается наш объект; платформа берет на себя задачу распространения оповещения.
Когда использовать?
Этот метод идеально подходит, когда табличная часть отображается на форме через динамический список, у которого задана основная таблица (например, сам справочник). Если мы программно изменяем объект (в том числе его табличную часть) и записываем его, то вызов ОповеститьОбИзменении() после записи поможет обновить отображение на других формах.
Разберем по шагам пример кода:
Предположим, у нас есть "Справочник1" с табличной частью "ТабличнаяЧасть1", и мы изменяем его из формы "Справочника2".
Сначала нам нужно изменить данные на сервере, а затем оповестить клиент о произошедших изменениях. Рассмотрим пример, который демонстрирует добавление новой строки в табличную часть и последующее оповещение:
// Модуль формы Справочника2 (или любой другой, где происходит изменение Справочника1)
&НаСервере
Процедура ДобавитьСтрокуВСправочник1НаСервере()
// Проверяем, что ссылка на Справочник1 заполнена
Если ЗначениеЗаполнено(Объект.Справочник1) Тогда
// Получаем объект Справочник1 для изменения
об = Объект.Справочник1.ПолучитьОбъект();
// Добавляем новую строку в табличную часть
НоваяСтрока = об.ТабличнаяЧасть1.Добавить();
НоваяСтрока.ДатаВремя = ТекущаяДата(); // Заполняем реквизит новой строки
// Записываем измененный объект Справочник1
об.Записать();
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ДобавитьСтрокуВСправочник1(Команда)
// Вызываем серверную процедуру для изменения данных
ДобавитьСтрокуВСправочник1НаСервере();
// Оповещаем об изменении объекта Справочник1.
// Если на других формах есть динамические списки,
// основанные на Справочнике1, они обновятся.
ОповеститьОбИзменении(Объект.Справочник1);
// Дополнительно можно обновить отображение данных текущей формы
ЭтаФорма.ОбновитьОтображениеДанных();
КонецПроцедуры
В этом примере мы видим, что сначала выполняется серверная процедура ДобавитьСтрокуВСправочник1НаСервере(), которая получает объект, изменяет его табличную часть и записывает. Ключевой момент: метод ОповеститьОбИзменении() вызывается на клиенте после записи объекта. Это гарантирует, что оповещение происходит уже о сохраненных данных.
Важное ограничение:
Следует помнить, что ОповеститьОбИзменении() не обновляет динамические списки, у которых не задана основная таблица. То есть, если ваш динамический список построен на произвольном запросе, который напрямую обращается к табличной части или делает сложные соединения, этот метод не сработает.
Оповестить() и ОбработкаОповещения()Когда ОповеститьОбИзменении() оказывается бессильным (например, для динамических списков с произвольным запросом или когда требуется более тонкий контроль), мы можем использовать механизм глобального оповещения.
Как это работает?
Метод глобального контекста Оповестить() позволяет отправить произвольное именованное событие с опциональными параметрами. На форме-приемнике мы реализуем обработчик события ОбработкаОповещения(), который "слушает" эти события. Внутри этого обработчика мы проверяем имя события и, если оно соответствует нашим ожиданиям, вызываем явное обновление нужного элемента формы, например, динамического списка.
Когда использовать?
Этот подход обеспечивает более гранулированный контроль над процессом обновления. Он незаменим для динамических списков, построенных на произвольных запросах, а также в случаях, когда нам нужно обновить не просто список, а какой-то специфический элемент формы или выполнить дополнительную логику.
Посмотрим на пример:
Предположим, у нас есть форма со списком, который должен обновиться, когда в другом месте произошли изменения. Мы назовем наше событие "ОбновитьДС".
На форме, которая должна обновиться (получатель оповещения):
// Модуль формы со списком (например, формы списка Справочника1)
&НаКлиенте
Процедура ОбработкаОповещения(ИмяСобытия, Параметр, Источник)
// Проверяем имя события
Если ИмяСобытия = "ОбновитьДС" Тогда
// Если это наше событие, то обновляем нужный динамический список
// Предполагаем, что элемент формы, отображающий список, называется "Список"
Элементы.Список.Обновить();
КонецЕсли;
// Можно добавить обработку других событий, если необходимо
КонецПроцедуры
В клиентском модуле, где происходит запись или изменение данных (отправитель оповещения):
// Клиентский модуль, где происходит изменение данных
// Например, это может быть процедура на форме Справочника2,
// которая вызвала серверный код изменения Справочника1
&НаКлиенте
Процедура ОбработатьЗадачуИОповестить()
// ... Здесь происходит вызов серверных процедур,
// которые изменяют данные и записывают их ...
// Например: ДобавитьСтрокуВСправочник1НаСервере();
// Оповещаем все открытые формы о событии "ОбновитьДС"
Оповестить("ОбновитьДС");
// Можно передать дополнительные параметры, если нужно:
// Оповестить("ОбновитьДС", МояСсылкаНаОбъект);
КонецПроцедуры
Мы видим, что метод Оповестить() вызывается без привязки к конкретному объекту, но с уникальным именем события. Это позволяет нам настроить гибкую систему уведомлений между различными частями приложения.
Иногда требуется обновить не только динамический список, но и другие элементы формы, или даже полностью перезагрузить данные объекта, лежащего в основе формы. Для этого существуют другие методы.
Обновление отображения данных формы: ЭтаФорма.ОбновитьОтображениеДанных()
Метод ЭтаФорма.ОбновитьОтображениеДанных() обновляет все данные, отображаемые на текущей форме, основываясь на её текущем состоянии. Он перезагружает значения всех элементов управления формы. Это может быть полезно, когда изменения затронули не только табличную часть, но и другие реквизиты объекта, и вы хотите, чтобы все они обновились.
Пример использования:
&НаКлиенте
Процедура КомандаОбновитьФорму(Команда)
// ... Выполняем действия по изменению данных ...
// Обновляем отображение всех данных на текущей форме
ЭтаФорма.ОбновитьОтображениеДанных();
КонецПроцедуры
Перезагрузка объекта данных формы: ЭтаФорма.Прочитать()
Метод ЭтаФорма.Прочитать() перезагружает объект данных формы из базы данных. Это крайне полезно, если сам объект, который является основой формы (например,
Объект
Явное перечитывание данных для ТаблицаЗначений
Если табличная часть отображается не через динамический список, а напрямую связана с элементом формы типа "ТаблицаЗначений" (например, реквизит формы типа
ТаблицаЗначений
// Предположим, что у формы есть реквизит "ТаблицаДляОтображения" типа ТаблицаЗначений
// и объект формы - это Справочник1
&НаСервере
Процедура ОбновитьТаблицуЗначенийНаСервере()
// Получаем актуальный объект из базы данных
АктуальныйОбъект = Объект.ПолучитьОбъект();
// Копируем табличную часть из актуального объекта в реквизит формы
Объект.ТаблицаДляОтображения.Загрузить(АктуальныйОбъект.ТабличнаяЧасть1.Выгрузить());
КонецПроцедуры
&НаКлиенте
Процедура ОбновитьТаблицуЗначений(Команда)
ОбновитьТаблицуЗначенийНаСервере();
КонецПроцедуры
ПодключитьОбработчикОжидания()В некоторых сценариях, особенно когда изменения могут происходить извне (например, другим пользователем в другом сеансе, или фоновым заданием), мгновенное оповещение может быть недостаточным или невозможным. В таких случаях мы можем реализовать механизм периодического опроса изменений с помощью ПодключитьОбработчикОжидания().
Как это работает?
Метод ПодключитьОбработчикОжидания() позволяет запланировать выполнение определенной процедуры через заданный интервал времени. Мы можем использовать его для периодического вызова серверной функции, которая проверяет, были ли изменения в данных, и при необходимости обновляет форму или динамический список.
Когда использовать?
Этот подход является своего рода "страховкой" и применяется, когда другие методы не дают гарантии актуальности данных. Например, если мы хотим, чтобы форма автоматически обновлялась каждые 10 секунд, если другой пользователь изменил данные. Важно: использовать этот механизм следует обдуманно, так как частое обращение к серверу может создавать излишнюю нагрузку.
Пример концепции:
// В модуле формы
&НаКлиенте
Перем ИдентификаторОбработчикаОжидания;
&НаКлиенте
Процедура ПриОткрытии()
// Подключаем обработчик ожидания при открытии формы
// Процедура "ОбновитьДанныеПоТаймеру" будет вызываться каждые 10 секунд
ИдентификаторОбработчикаОжидания = ПодключитьОбработчикОжидания("ОбновитьДанныеПоТаймеру", 10, Истина);
КонецПроцедуры
&НаКлиенте
Процедура ПриЗакрытии()
// Отключаем обработчик ожидания при закрытии формы, чтобы избежать утечек памяти
Если ИдентификаторОбработчикаОжидания <> Неопределено Тогда
ОтключитьОбработчикОжидания(ИдентификаторОбработчикаОжидания);
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ОбновитьДанныеПоТаймеру()
// Здесь можно реализовать логику проверки изменений и обновления
// Например, можно вызвать серверную функцию, которая сравнит текущую версию
// объекта с сохраненной в базе или просто обновит динамический список.
// Пример: Простое обновление динамического списка
// Элементы.Список.Обновить();
// Или, если есть механизм отслеживания изменений:
// Если ЕстьИзмененияНаСервере() Тогда
// ЭтаФорма.ОбновитьОтображениеДанных();
// КонецЕсли;
КонецПроцедуры
Выбор способа отображения и обновления табличной части во многом зависит от её размера и сложности данных. Давайте проанализируем эти аспекты:
Для небольших табличных частей: Если табличная часть содержит относительно небольшое количество строк (например, до нескольких сотен или тысяч), можно использовать обычную ТаблицаЗначений в качестве реквизита формы и заполнять её программно. При этом обновление будет требовать явного перечитывания и перезаполнения.
Для больших табличных частей: Если табличная часть может содержать тысячи и более строк, то предпочтительнее использовать динамический список. Он обеспечивает эффективную работу с большими объемами данных за счет подгрузки только видимой части и выполнения запросов на сервере. В таких случаях методы ОповеститьОбИзменении() или связка Оповестить() + ОбработкаОповещения() + Элементы.Список.Обновить() будут наиболее уместны.
Очень большие объемы данных или сложная аналитика: В некоторых случаях, когда табличная часть становится очень большой, активно используется для аналитики или требует сложной индексации, может быть целесообразно вынести её в отдельный регистр сведений. Это позволяет оптимизировать работу с данными, но требует пересмотра логики работы с объектом-владельцем.
Взаимодействие между объектами: Если мы часто сталкиваемся с ситуацией, когда табличная часть одного справочника программно изменяется из другого справочника или документа, это может указывать на архитектурную проблему. Мы рекомендуем проанализировать такую логику: возможно, эти объекты слишком тесно связаны, или же часть данных должна быть вынесена в отдельный объект. Правильная архитектура поможет не только упростить обновление интерфейса, но и обеспечить целостность данных.
Надеемся, что этот подробный разбор поможет вам эффективно решать задачи по корректному отображению и обновлению табличных частей реквизитов в ваших 1С-проектах!
← К списку