В разработке на платформе 1С часто возникает задача: открыть из основной формы вспомогательную (дочернюю), ввести в ней какие-либо данные, а затем вернуть результат обратно в основную форму. Рассмотрим на практическом примере, как реализовать этот механизм, разберем типичные ошибки начинающих разработчиков и освоим стандартный, рекомендуемый способ решения этой задачи. Если ваша задача выходит за рамки простого обмена данными и требует более глубокой кастомизации интерфейса, вы можете изучить программное изменение форм с помощью готовых инструментов — для этой цели есть конструктор произвольных форм ввода данных без конфигуратора.
Проанализируем ситуацию: разработчик пытается создать форму для ввода адреса по частям (город, улица, дом). После нажатия кнопки данные должны объединяться в одну строку и передаваться в поле на основной форме. Однако при попытке объединить значения возникает ошибка: «Невозможно преобразовать значение к типу число».
Первопричина ошибки кроется в фундаментальном различии между реквизитами и элементами формы, которое часто вызывает путаницу у новичков. Давайте разберемся в этих понятиях.
В исходном коде была допущена ключевая ошибка — попытка сложить не значения, а сами элементы управления:
// НЕПРАВИЛЬНО
ЭтаФорма.Параметры.ПараметрГород = ЭтаФорма.Элементы.ПолеВводаГород;
...
СтрокаАдрес = ЭтаФорма.Параметры.ПараметрГород + ", ул. " + ЭтаФорма.Элементы.ПолеВводаУлица + ЭтаФорма.Элементы.ПолеВводаДом;
Платформа 1С не знает, как складывать между собой объект «Поле ввода» и другой объект «Поле ввода». Отсюда и возникает ошибка. Чтобы получить значения, нужно обращаться напрямую к реквизитам формы, которые связаны с этими элементами. Также не забывайте, что в реальных задачах бывает важно настраивать доступность элементов формы у пользователей, чтобы ограничить редактирование определенных полей.
Кроме того, ошибка «Невозможно преобразовать значение к типу число» возникает, когда один из реквизитов (например, номер дома) имеет числовой тип. При попытке сложить строки с числом с помощью оператора + (конкатенации) платформа может пытаться выполнить арифметическое сложение, что приводит к ошибке преобразования типов. Чтобы этого избежать, числовые значения нужно явно преобразовывать в строку с помощью функции Строка().
Это один из возможных механизмов, который работает по принципу «широковещательной рассылки». Одна форма отправляет сигнал, а все остальные открытые формы могут его «услышать» и обработать. Разберем его по шагам.
Открытие дочерней формы
В модуле основной формы создаем команду, которая просто открывает нашу форму для ввода адреса.
&НаКлиенте
Процедура ВвестиАдрес(Команда)
ОткрытьФорму("Справочник.ОбъектыСтроительства.Форма.ФормаЭлемента");
КонецПроцедуры
Формирование и отправка данных из дочерней формы
В дочерней форме по нажатию кнопки «Записать» мы собираем данные из реквизитов, формируем строку и отправляем оповещение. Обратите внимание, что здесь мы уже обращаемся к реквизитам (например, ЭтаФорма.ГородФорма), а не к элементам, и используем функцию Строка() для безопасного преобразования типов.
&НаКлиенте
Процедура КомандаЗаписатьАдрес(Команда)
// Получаем значения из реквизитов формы
Город = ЭтаФорма.ГородФорма;
Улица = ЭтаФорма.УлицаФорма;
Дом = ЭтаФорма.ДомФорма;
// Формируем строку адреса
СтрокаАдрес = "г. " + Строка(Город) + ", ул. " + Строка(Улица) + ", д. " + Строка(Дом);
// Оповещаем все открытые формы
Оповестить("ЗаполнениеАдреса", СтрокаАдрес, ЭтаФорма);
// ВАЖНО: Форма сама не закроется, нужно вызвать метод Закрыть()
Закрыть();
КонецПроцедуры
Важный момент: после вызова метода Оповестить() форма не закрывается автоматически. Чтобы закрыть ее, необходимо явно вызвать команду Закрыть().
Получение данных в основной форме
Чтобы основная форма «услышала» сигнал, в ее модуле должна быть экспортная процедура-обработчик ОбработкаОповещения.
&НаКлиенте
Процедура ОбработкаОповещения(ИмяСобытия, Параметр, Источник) Экспорт
// Проверяем имя события, чтобы реагировать только на нужные сигналы
Если ИмяСобытия = "ЗаполнениеАдреса" Тогда
// Устанавливаем полученное значение в реквизит "Адрес"
Адрес = Параметр;
Сообщить("Адрес получен: " + Параметр);
КонецЕсли;
КонецПроцедуры
Этот способ рабочий, но имеет недостатки: он создает неявные связи между формами. Любая открытая форма с обработчиком ОбработкаОповещения может перехватить сигнал, что иногда приводит к непредсказуемому поведению. Поэтому для сценария «вопрос-ответ» между двумя конкретными формами существует более предпочтительный метод.
Этот метод является современным стандартом, используемым в Библиотеке стандартных подсистем (БСП). Для глубокого изучения подобных механизмов рекомендуем использовать справочник по методам БСП, где собрано множество примеров кода — также в работе поможет инструмент для отладки и оптимизации кода на управляемых формах. Он устанавливает явную и прочную связь между вызывающей и вызываемой формами. Логика работы становится прозрачной и управляемой.
Вызов дочерней формы из основной
Теперь команда открытия формы выглядит сложнее, но гораздо функциональнее. Мы создаем специальный объект ОписаниеОповещения, в котором указываем, какую процедуру нашей основной формы нужно вызвать после закрытия дочерней.
&НаКлиенте
Процедура АдресНачалоВыбора(Элемент, ДанныеВыбора, ВыборДобавлением, СтандартнаяОбработка)
// Отменяем стандартное поведение, так как мы открываем свою форму
СтандартнаяОбработка = Ложь;
// Создаем структуру для передачи параметров В дочернюю форму (например, для редактирования)
ПараметрыВвода = Новый Структура("Адрес", Адрес);
// Создаем оповещение для получения результата.
// Указываем имя процедуры-обработчика ("ПослеВыбораАдреса") и текущую форму (ЭтаФорма)
ОписаниеОповещения = Новый ОписаниеОповещения("ПослеВыбораАдреса", ЭтаФорма);
// Открываем форму с параметрами и оповещением
ОткрытьФорму("ВнешняяОбработка.ПримерРаботыСФормойВводаАдреса.Форма.ФормаВводаАдреса",
ПараметрыВвода,
Элемент,
,
,
,
ОписаниеОповещения,
РежимОткрытияОкнаФормы.БлокироватьОкноВладельца);
КонецПроцедуры
Работа в дочерней форме
В дочерней форме мы можем принять переданные параметры при создании на сервере. Это удобно для редактирования уже существующего адреса.
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Разбираем переданный адрес на части для заполнения полей
АдресСтрокой = Параметры.Адрес;
Если Не ПустаяСтрока(АдресСтрокой) Тогда
// Здесь может быть логика разбора строки на Город, Улицу, Дом и т.д.
// ...
КонецЕсли;
КонецПроцедуры
Самое главное изменение — в команде «Записать» (или «ОК»). Вместо Оповестить() мы используем метод Закрыть(), передавая в него возвращаемое значение.
&НаКлиенте
Процедура КомандаОК(Команда)
// Собираем адрес из реквизитов формы
Адрес = СокрЛП(Регион) + ", " + СокрЛП(Город) + ", " + СокрЛП(Улица)+ ", " + СокрЛП(Дом);
// Закрываем форму и передаем собранную строку в качестве результата
Закрыть(Адрес);
КонецПроцедуры
Получение результата в основной форме
Теперь в основной форме будет вызвана та самая процедура, которую мы указали в ОписаниеОповещения. Значение, переданное в метод Закрыть(), придет в первый параметр этой процедуры.
&НаКлиенте
Процедура ПослеВыбораАдреса(Результат, ДопПараметры) Экспорт
// Если пользователь закрыл форму без выбора (нажал крестик), результат будет Неопределено
Если Результат = Неопределено Тогда
Возврат;
КонецЕсли;
// Присваиваем полученное значение реквизиту основной формы
Адрес = Результат;
КонецПроцедуры
Изящное форматирование строк. Вместо ручного сложения строк через +, удобнее и нагляднее использовать функцию СтрШаблон(). Код становится чище и читабельнее.
СтрокаАдрес = СтрШаблон("%1, ул. %2, д. %3", Город, Улица, Дом);
Возврат структурированных данных. Часто бывает полезнее вернуть из дочерней формы не готовую строку, а Структуру или Массив с частями адреса. Это дает вызывающей форме больше гибкости: она может как собрать полную строку, так и использовать отдельные части для других целей. Для автоматизации тестирования таких сложных сценариев взаимодействия форм можно использовать Yet Another Form Modificator, который позволяет генерировать автотесты на основе описания модификаций.
В дочерней форме:
РезультатВозврата = Новый Структура("Город, Улица, Дом", Город, Улица, Дом);
Закрыть(РезультатВозврата);
В основной форме:
&НаКлиенте
Процедура ПослеВыбораАдреса(Результат, ДопПараметры) Экспорт
Если Результат = Неопределено Тогда
Возврат;
КонецЕсли;
// Теперь мы можем делать с данными что угодно
Адрес = Результат.Город + ", " + Результат.Улица + ", " + Результат.Дом;
ЭтотОбъект.Город = Результат.Город; // Сохранить город в отдельный реквизит
КонецПроцедуры
Подводя итог, для обмена данными между формами настоятельно рекомендуется использовать второй способ с ОписаниеОповещения. Он делает код предсказуемым, легко читаемым и соответствующим современным стандартам разработки на платформе 1С.