Как создать динамический список с расширенным форматированием и сохранить прокрутку в 1С?

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

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

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

Ограничения стандартного динамического списка и формулировка задачи

Начнем с анализа того, что мы хотим получить. Мы стремимся к созданию списка, который будет:

  1. Отображать элементы с филигранным, нестандартным оформлением для каждой строки. Например, это может быть список задач, где каждая задача отображается с подзадачами, образуя бесконечную иерархию.
  2. Сохранять возможности прокрутки, присущие динамическому списку, чтобы эффективно работать с десятками тысяч позиций справочника или документа без необходимости выводить их все сразу.
  3. Быть интерактивным, позволяя взаимодействовать с отображаемыми элементами, возможно, даже с иерархической структурой внутри каждой строки.

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

Подходы к реализации расширенного оформления в 1С

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

1. Улучшение оформления стандартного динамического списка с помощью серверных событий

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

Использование события «ПриПолученииДанныхНаСервере»

Событие ПриПолученииДанныхНаСервере для динамического списка, доступное начиная с версии платформы 8.3.10, позволяет нам дополнительно обрабатывать данные, полученные для отображения. Это событие вызывается на сервере только для видимых строк списка, что обеспечивает дополнительную оптимизацию при работе с большими объемами данных.

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


&НаСервере
Процедура СписокПриПолученииДанныхНаСервере(Элемент, Данные, Отказ)
    Для Каждого Строка Списка.Элементы Цикл
        // Пример: Если есть поле "СтатусЗадачи"
        Если Строка.СтатусЗадачи = Перечисления.СтатусыЗадач.Выполнена Тогда
            // Изменяем цвет текста для всей строки
            Строка.ЦветТекста = ЦветаСтиля.ЦветВыполненнойЗадачи;
            // Или для конкретной ячейки
            Строка.Ячейки.Наименование.ЦветТекста = ЦветаСтиля.ЦветВыполненнойЗадачи;
        КонецЕсли;
        
        // Пример: Добавляем форматированное представление для поля, которого нет в запросе
        // Предположим, что в запросе есть поля "ДатаНачала" и "ДатаОкончания"
        Строка.ПредставлениеПериода = "с " + Формат(Строка.ДатаНачала, "ДФ=dd.MM.yyyy") + 
                                       " по " + Формат(Строка.ДатаОкончания, "ДФ=dd.MM.yyyy");
        // Это поле "ПредставлениеПериода" должно быть добавлено в колонки динамического списка
        // в конфигураторе или программно.
    КонецЦикла;
КонецПроцедуры

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

Свойство «ВыражениеПредставления»

Начиная с версии 8.3.20, у полей динамического списка появилось новое свойство ВыражениеПредставления. Если это свойство задано, динамический список использует его для формирования отображаемого текста в таблице (похожий принцип использует форматированный документ). Это позволяет нам формировать более сложное текстовое представление поля, объединяя данные из нескольких полей или применяя форматирование, без изменения основного запроса или дополнительного кода в ПриПолученииДанныхНаСервере.

Например, вместо того, чтобы выводить отдельные колонки "Фамилия", "Имя", "Отчество", мы можем создать одну колонку "ПолноеИмя" с выражением представления: [Фамилия] + " " + [Имя] + " " + [Отчество].

Условное оформление

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

  1. В конфигураторе перейдите к элементу формы типа ТабличноеПоле, отображающему динамический список.
  2. Откройте свойства этого элемента и найдите раздел УсловноеОформление.
  3. Добавьте новый элемент условного оформления. Укажите Оформляемые поля (например, "Наименование" или "ВсяСтрока"), Условие (например, СтатусЗадачи = &СтатусВыполнена) и Параметры оформления (например, ЦветТекста, ЦветФона, Шрифт).

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

2. Использование поля HTML-документа для интерактивности и сложного вывода

Если нам требуется действительно "филигранное" оформление, полная свобода в разметке и высокая интерактивность, то наилучшим решением является использование элемента формы Поле HTML документа. По сути, это встроенный браузерный движок Webkit, который позволяет интегрировать HTML, CSS и JavaScript, открывая широчайшие возможности для создания динамичных и интерактивных элементов интерфейса.

Преимущества Поля HTML документа
  1. Произвольная разметка: Мы можем использовать любой HTML-код для создания сложного макета каждой строки, включая таблицы, DIV-блоки, изображения, SVG-графику и многое другое.
  2. Интерактивность: С помощью JavaScript мы можем реализовать любую клиентскую логику, обработку событий (клики, наведение курсора, изменение данных), создавать динамические элементы, анимацию. JavaScript также может запускать код, который нельзя реализовать на языке 1С, и организовывать обмен данными между клиентом и сервером.
  3. Гибкость стилизации: CSS дает полную свободу в оформлении, позволяя добиться любого визуального эффекта.
  4. Примеры использования: Многие крупные разработки, например, "Рарус CRM", используют Поле HTML документа для отображения красивых лент взаимодействий или других сложных интерфейсов.
Решение проблемы прокрутки и больших данных с Полем HTML документа

Основная проблема при использовании Поля HTML документа вместо динамического списка для отображения большого объема данных – это прокрутка. Если мы выведем 10 000 позиций справочника сразу в HTML-документ, форма будет "тормозить" или даже зависнет. Для этого нам потребуется реализовать механизмы виртуализации или пагинации, аналогичные тем, что используются в веб-приложениях.

  1. Программное формирование HTML: На сервере 1С мы можем программно формировать HTML-документ, содержащий только часть данных, которая должна быть видна на экране, плюс небольшой буфер сверху и снизу.
  2. Обработка событий прокрутки в JavaScript: Внутри HTML-додокумента мы можем написать JavaScript-код, который будет отслеживать событие прокрутки. Когда пользователь приближается к концу прокручиваемой области (или к началу, если прокручивает вверх), JavaScript может уведомить платформу 1С о необходимости загрузить новые элементы.
  3. Обмен данными с сервером 1С: Для обмена данными между JavaScript и 1С мы можем использовать специальные механизмы:
    • Вызов методов глобального контекста 1С из JavaScript (например, window.external.call("МойМетодНаКлиенте", "Параметр")), который, в свою очередь, может вызвать серверный метод.
    • Изменение значения реквизита формы, связанного с полем HTML-документа, и обработка события ПриИзменении этого реквизита.
  4. "Ленивая" загрузка (Lazy Loading) или виртуализация: При получении сигнала о необходимости загрузить новые данные, сервер 1С формирует дополнительный HTML-фрагмент с новыми элементами и передает его обратно в JavaScript, который динамически добавляет его в DOM текущего HTML-документа. Это позволяет работать с большим количеством позиций без необходимости выводить их все сразу.
  5. Формирование интерактивных ссылок: В поле HTML-документа мы можем программно формировать HTML-документ, содержащий ссылки на объекты базы данных 1С (например, <a href="e1c://Объект/Справочник.Номенклатура?Ссылка=GUID_объекта">). При клике по таким ссылкам платформа 1С может обрабатывать их, открывая соответствующие формы объектов.

// Пример JavaScript-кода в HTML-документе для обработки прокрутки
// (фрагмент, требующий доработки и адаптации)

var scrollContainer = document.getElementById('myScrollableContent');

scrollContainer.onscroll = function() {
    if (scrollContainer.scrollTop + scrollContainer.clientHeight >= scrollContainer.scrollHeight - 100) {
        // Достигли почти конца, запрашиваем новые данные
        if (window.external && window.external.call) {
            window.external.call('ЗагрузитьСледующиеЭлементы', 'Параметр_для_сервера');
        }
    }
    // Можно также отслеживать прокрутку вверх
    if (scrollContainer.scrollTop < 100 && scrollContainer.dataset.hasPrevious === 'true') {
        if (window.external && window.external.call) {
            window.external.call('ЗагрузитьПредыдущиеЭлементы', 'Параметр_для_сервера');
        }
    }
};

// Функция для добавления нового HTML-контента
function addContent(htmlContent) {
    scrollContainer.innerHTML += htmlContent; // Или более аккуратно вставлять
}

// Пример кода 1С для обработки вызова из JavaScript
// (в модуле формы)

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

&НаСервере
Процедура ПолучитьИДобавитьДанныеНаСервере(Параметр)
    // Получаем следующую порцию данных
    НовыеДанные = ЗапросКБазеДанных(Параметр);
    
    // Формируем HTML-фрагмент
    НовыйHTML = СформироватьHTML(НовыеДанные);
    
    // Передаем HTML-фрагмент на клиент и вызываем JS-функцию
    Элементы.ПолеHTMLДокумента1.ВыполнитьКоманду("addContent('" + НовыйHTML + "');");
КонецПроцедуры

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

3. Использование элемента "Дерево на управляемых формах"

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

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

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


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

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

4. Формирование табличного документа с прокруткой

Ещё один подход – это использование ТабличногоДокумента для вывода сложного макета. Мы можем сформировать каждую строку списка в виде отдельной области табличного документа, используя различные макеты.

Создание ТабличногоДокумента

Мы можем использовать систему компоновки данных (СКД) с оформленным макетом, чтобы сгенерировать сложный отчет, который будет представлять наш список в виде табличного документа. Затем этот ДокументРезультат (т.е. сформированный ТабличныйДокумент) мы можем вывести на форму в элементе Поле Табличного Документа.


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

Табличный документ позволяет очень гибко настраивать внешний вид, объединять ячейки, использовать различные шрифты и цвета, выводить картинки. Однако, как и в случае с HTML-документом, возникает проблема прокрутки для больших объемов данных. Если мы выведем все 10 000 позиций сразу в один ТабличныйДокумент, это приведет к серьезным "тормозам".

Решение проблемы прокрутки в ТабличномДокументе

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

  1. Мы должны будем обрабатывать событие прокрутки элемента Поле Табличного Документа на форме (например, через JavaScript-обработчик или средствами 1С, если такие события доступны, или косвенно через изменение видимой области).
  2. При достижении "границы" видимости мы будем запрашивать новые порции данных с сервера.
  3. Сервер будет формировать дополнительный фрагмент табличного документа (или набор строк для добавления) и передавать его на клиент.
  4. На клиенте мы будем программно добавлять эти новые строки к уже существующему ТабличномуДокументу.

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

Также мы можем выводить произвольную расшифровку под динамическим списком для текущей активной строки ДС. Однако это не соответствует изначальной задаче, где клиенту нужно видеть "расшифровку" для каждой строки непосредственно в списке.

Особенности работы с динамическими списками и производительность

Независимо от выбранного подхода, нам необходимо учитывать общие рекомендации по производительности 1С:

  1. Оптимизация запросов: Динамические списки по умолчанию считывают данные порциями, что обеспечивает быструю работу с большими объемами информации. Однако при использовании произвольных запросов важно правильно указать основную таблицу и ключевые поля. Иначе платформа не сможет эффективно считывать данные динамически, что может привести к снижению производительности. Избегайте "сложных" запросов, которые могут замедлять работу, например, запросов с большим количеством подзапросов или соединений, которые могут приводить к полному сканированию таблиц.
  2. Механизм полосы прокрутки: Механизм полосы прокрутки в табличном поле, отображающем динамический список, имеет свои особенности: бегунок отображается только в трех положениях (вверху, в центре, внизу) и имеет постоянный размер, так как загружается только видимая часть списка. Это может сбивать с толку пользователей, привыкших к "настоящей" прокрутке.
  3. Вертикальная прокрутка формы: Для форм, содержащих поля для вывода HTML-документов или табличных документов, рекомендуется устанавливать свойство "Вертикальная прокрутка" формы в значение "Использовать при необходимости". Это позволяет платформе автоматически управлять прокруткой всей формы, если содержимое элемента формы (например, HTML-документа) превышает видимую область.
  4. Избегайте крохоборства: Многие вещи, например, выводить Наименование вместо ссылки, считаются крохоборством в плане оптимизации, но из них и складываются "тормоза". То есть не конкретно в чём-то ошибка проектирования, а именно из таких мелких "недочётов" и складывается общая низкая производительность. Всегда стремитесь к максимально эффективному использованию ресурсов.

Таким образом, для достижения "филигранного" оформления с сохранением прокрутки, мы можем комбинировать подходы: использовать ПриПолученииДанныхНаСервере и ВыражениеПредставления для улучшения оформления стандартного динамического списка. Но для более сложных, интерактивных иерархических элементов нам придется рассмотреть интеграцию Поля HTML документа с собственной логикой прокрутки и загрузки данных через JavaScript или использовать Дерево на управляемых формах для иерархических данных.

← На главную