При разработке интерфейсов на управляемых формах в платформе «1С:Предприятие 8.3» программисты часто сталкиваются с задачей сортировки данных в табличных частях. Начинающие разработчики нередко путают поведение динамического списка и таблицы значений, выведенной на форму. В этой статье мы подробно разберем, почему в таблице значений на управляемой форме по умолчанию отсутствуют интерактивные стрелочки сортировки в шапке колонок, как решить эту задачу стандартными средствами платформы, как реализовать быструю программную сортировку на клиенте и как оптимизировать этот процесс в соответствии со стандартами разработки фирмы «1С». ---
Для начала выясним причину, почему поведение этих двух объектов при выводе на форму так сильно отличается.
Динамический список (ДС) спроектирован для работы с большими массивами данных, хранящимися в базе данных. Под капотом ДС работает Система компоновки данных (СКД). Когда пользователь кликает по заголовку колонки в динамическом списке, платформа автоматически инициирует генерацию нового SQL-запроса к СУБД с конструкцией ORDER BY. Сортировка здесь является постоянным режимом отображения данных.
Таблица значений (ТЗ), отображаемая на форме через связанный реквизит (тип ДанныеФормыКоллекция), представляет собой статический набор данных, уже загруженный в оперативную память. Сортировка в таблице значений — это разовое действие. После того как строки отсортированы, пользователь может программно или интерактивно перемещать их вверх-вниз, добавлять новые строки в произвольные места или удалять их. Из-за этой архитектурной особенности платформа по умолчанию не отображает стрелки направления сортировки в заголовках колонок таблицы значений.
---
Если на форме размещена таблица, связанная с таблицей значений или табличной частью, пользователь все равно может отсортировать её без написания какого-либо программного кода. Для этого используются стандартные команды платформы:
---
Довольно распространенным заблуждением является мнение, что для программной сортировки таблицы значений на управляемой форме обязательно требуется делать контекстный вызов сервера, преобразовывать коллекцию формы в объект базы данных и затем возвращать её обратно. Это не так.
Рассмотрим подробнее объект ДанныеФормыКоллекция, который выступает типом данных для табличных частей и таблиц значений на форме. У этого объекта есть собственный метод Сортировать(), доступный непосредственно в клиентском контексте. Это позволяет выполнять сортировку мгновенно без нагрузки на сервер и без лишней сериализации данных.
Разберем по шагам, как выполнить программную сортировку на клиенте:
&НаКлиенте
Процедура СортироватьТаблицуНаКлиенте()
// Сортировка по одной колонке по возрастанию
Объект.Товары.Сортировать("Номенклатура Возр");
// Пример многоуровневой сортировки (по нескольким колонкам)
Объект.Товары.Сортировать("Склад Возр, Количество Убыв");
КонецПроцедуры
Использование этого метода на клиенте работает чрезвычайно быстро, так как платформа оптимизирует перестроение индексов визуального отображения строк непосредственно в браузере или тонком клиенте.
---
Если бизнес-требования обязывают сделать так, чтобы пользователь мог сортировать таблицу значений простым кликом по заголовку колонки (как в динамическом списке), мы можем применить обходной путь.
Для этого мы задействуем обработчик события таблицы Выбор или ПриАктивизацииЯчейки, либо добавим собственные кнопки в командную панель. Рассмотрим вариант с использованием события изменения активной колонки для имитации клика.
Разберем пример реализации, в котором мы будем отслеживать клик по колонке и менять направление сортировки:
&НаКлиенте
Перем ТекущееНаправлениеСортировки; // Хранит направление сортировки
&НаКлиенте
Процедура ТоварыПриАктивизацииЯчейки(Элемент)
ТекущаяКолонка = Элемент.ТекущийЭлемент.Имя;
// Исключаем системные колонки, которые не нужно сортировать
Если ТекущаяКолонка = "ТоварыНомерСтроки" Тогда
Возврат;
КонецЕсли;
// Определяем имя реквизита (убираем префикс пути к данным, если необходимо)
ИмяПоляДляСортировки = Элемент.ТекущийЭлемент.ПутьКДанным;
// Например, из "Объект.Товары.Номенклатура" получаем "Номенклатура"
ЧастиПути = СтрРазделить(ИмяПоляДляСортировки, ".");
ИмяКолонки = ЧастиПути[ЧастиПути.ВГраница()];
// Меняем направление сортировки при повторном клике на ту же колонку
Если ТекущееНаправлениеСортировки = "Возр" Тогда
ТекущееНаправлениеСортировки = "Убыв";
Иначе
ТекущееНаправлениеСортировки = "Возр";
КонецЕсли;
// Выполняем сортировку коллекции на клиенте
Объект.Товары.Сортировать(ИмяКолонки + " " + ТекущееНаправлениеСортировки);
КонецПроцедуры
---
Иногда разработчики пытаются пойти от обратного: вместо того чтобы настраивать ТЗ, они хотят "засунуть" её в динамический список, чтобы использовать все плюсы СКД (включая автоматические стрелочки сортировки и быстрые отборы).
Важное техническое ограничение: Передать напрямую таблицу значений в качестве параметра запроса динамического списка нельзя. При попытке сделать это платформа выбросит ошибку сериализации XDTO. Динамический список должен уметь свободно передавать свои настройки между клиентом и сервером, а тип данных ТаблицаЗначений доступен только на сервере и не может быть сериализован для передачи на клиент в параметрах ДС.
Тем не менее, существуют правильные архитектурные способы обхода этого ограничения:
Если ваша таблица значений содержит список ссылок (например, документов или элементов справочника), вы можете выгрузить эти ссылки в массив. Массивы простых типов отлично сериализуются и передаются в параметры динамического списка.
В запросе динамического списка пишется стандартное условие отбора:
ВЫБРАТЬ
ДокументТаблица.Ссылка,
ДокументТаблица.Дата,
ДокументТаблица.Номер
ИЗ
Документ.РеализацияТоваровУслуг КАК ДокументТаблица
ГДЕ
ДокументТаблица.Ссылка В (&МассивДокументов)
Если данных действительно много и они не ограничиваются простым списком ссылок, хорошей практикой является использование служебного справочника или регистра сведений. Данные из ТЗ записываются в этот регистр с привязкой к уникальному идентификатору текущего сеанса (или специальному ключу). Динамический список настраивается на чтение этого регистра с простым отбором по вашему ключу. По окончании работы данные очищаются. ---
Когда мы работаем с большими таблицами значений на стороне сервера, критически важно соблюдать требования к производительности. Стандарты разработки фирмы «1С» накладывают строгие ограничения на сортировку таблиц значений по колонкам, содержащим ссылочный тип данных (например, ссылки на справочники или документы).
Выясним причину проблемы: Если выполнить простую команду ТаблицаЗначений.Сортировать("Номенклатура"), платформа для каждой сравниваемой строки будет выполнять неявные запросы к базе данных, чтобы получить текстовое представление (наименование) этой номенклатуры для упорядочивания по алфавиту. Если в таблице несколько тысяч строк, это приведет к катастрофическому падению производительности и зависанию системы.
Для правильного решения этой задачи разработчики используют два основных подхода:
Если нам действительно важен точный алфавитный порядок представлений, то еще на этапе заполнения таблицы значений запросом мы должны получить текстовое представление ссылки и записать его в отдельную колонку. Сортировка затем производится по этому текстовому полю.
// Пример запроса, подготавливающего данные для быстрой сортировки
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Товары.Ссылка КАК Номенклатура,
| Товары.Наименование КАК НоменклатураПредставление,
| Товары.Код КАК Код
|ИЗ
| Справочник.Номенклатура КАК Товары";
ТаблицаДанных = Запрос.Выполнить().Выгрузить();
// Сортируем по текстовому представлению - СУБД больше не опрашивается!
ТаблицаДанных.Сортировать("НоменклатураПредставление Возр");
Если точный алфавитный порядок наименований нам не принципиален, но нужно быстро сгруппировать или упорядочить строки по ссылкам, мы должны использовать специальный объект платформы СравнениеЗначений. В этом случае сравнение будет происходить по внутреннему уникальному идентификатору ссылки (UUID), что выполняется мгновенно в оперативной памяти без обращения к СУБД.
// Создаем объект сравнения значений
ОбъектСравнения = Новый СравнениеЗначений;
// Выполняем быструю и безопасную сортировку по ссылочному полю
ТаблицаДанных.Сортировать("Номенклатура", ОбъектСравнения);
---
Мы подробно разобрали особенности работы механизмов сортировки в различных объектах интерфейса 1С. Подведем итог:
Сортировать() объекта ДанныеФормыКоллекция прямо на клиенте, не прибегая к серверным вызовам.СравнениеЗначений или предварительно подготовленные текстовые колонки представлений, чтобы избежать неявных ресурсоемких запросов к базе данных.