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