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