Как сократить однотипный код для множества флажков или ответов на форме в 1С

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

При разработке интерактивных форм, таких как тесты, опросы или сложные анкеты, программисты часто сталкиваются с лавинообразным ростом объема кода. Представьте ситуацию: на форме расположено несколько десятков вопросов, у каждого из которых есть по четыре варианта ответа, выполненных в виде флажков. Если писать для каждого флажка отдельную процедуру ПриИзменении, которая регулирует доступность остальных вариантов, то для 100 вопросов вам потребуется написать и поддерживать 400 практически одинаковых процедур!

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

Почему ручная привязка процедур — это тупиковый путь?

Давайте проанализируем стандартный "лобовой" подход, который часто используют начинающие разработчики. Для каждого флажка (например, Ответ1, Ответ2 и т.д.) создается собственное событие на клиенте. При установке флага в положение Истина код программно отключает доступность всех остальных связанных флажков этой группы. При снятии флага — возвращает доступность обратно.

Этот алгоритм жизнеспособен, когда у вас всего один вопрос. Но как только их количество увеличивается, разработчик сталкивается со следующими проблемами:

  1. Огромный объем кода: Форма превращается в "простыню" из сотен одинаковых процедур.
  2. Сложность поддержки: Если понадобится изменить логику поведения (например, не скрывать элементы, а делать их неактивными по другому условию), придется вручную переписывать каждую процедуру.
  3. Высокая вероятность ошибок: При копировании кода легко пропустить или перепутать имена элементов (например, вместо Ответ1_3 заблокировать Ответ2_3).

Выясним, как решить эту задачу изящно и профессионально с помощью различных подходов.

Решение 1. Переход на Переключатели (наиболее правильный подход)

Прежде чем приступать к написанию универсального программного кода, проанализируем саму архитектуру интерфейса. Если логика вашей задачи подразумевает, что пользователь может выбрать только один вариант из нескольких предложенных, то использование флажков (CheckBox) является методологической ошибкой проектирования интерфейсов. Для этих целей в UI/UX дизайне и в платформе 1С существуют Переключатели (RadioButtons).

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

Рассмотрим по шагам, как это настроить в конфигураторе:

  1. Создайте на форме один реквизит (а не четыре!). Назовите его, например, НомерВыбранногоОтвета с типом данных Число.
  2. Перетащите этот реквизит на рабочую область формы.
  3. В свойствах созданного элемента формы измените свойство Вид с значения Поле ввода на Поле переключателя.
  4. Перейдите в свойство Список выбора и заполните его вариантами. Например: Значение 1 — Представление "Вариант А"; Значение 2 — Представление "Вариант Б"; Значение 3 — Представление "Вариант В".

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

Решение 2. Создание одной универсальной процедуры на клиенте

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

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

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

Назначим всем этим флажкам в качестве обработчика события ПриИзменении одну и ту же процедуру ОтветПриИзменении. Разберем код этой процедуры подробнее:


&НаКлиенте
Процедура ОтветПриИзменении(Элемент)
    
    // Получаем имя элемента, на который нажал пользователь
    ИмяТекущегоЭлемента = Элемент.Имя;
    
    // Определяем текущее состояние флажка (Истина или Ложь)
    ЗначениеФлажка = Объект[ИмяТекущегоЭлемента];
    
    // Выделяем номер ответа (последний символ имени)
    ДлинаИмени = СтрДлина(ИмяТекущегоЭлемента);
    НомерЭлемента = Число(Прав(ИмяТекущегоЭлемента, 1));
    
    // Выделяем базовое имя (префикс без номера)
    БазовоеИмя = Лев(ИмяТекущегоЭлемента, ДлинаИмени - 1);
    
    // Предположим, что в каждом вопросе у нас ровно 4 варианта ответа
    Для НомерВарианта = 1 По 4 Цикл
        
        // Пропускаем элемент, на который нажал пользователь
        Если НомерВарианта = НомерЭлемента Тогда
            Продолжить;
        КонецЕсли;
        
        // Формируем имя соседнего элемента
        ИмяСоседа = БазовоеИмя + НомерВарианта;
        
        // Управляем доступностью соседних элементов. 
        // Если галочка поставлена, соседи блокируются (Доступность = Ложь).
        // Если галочка снята, соседи снова становятся доступны (Доступность = Истина).
        Элементы[ИмяСоседа].Доступность = НЕ ЗначениеФлажка;
        
    КонецЦикла;
    
КонецПроцедуры

Этот лаконичный код полностью заменяет сотни строк ручной блокировки. Он одинаково успешно будет обслуживать и Вопрос1_Ответ1, и Вопрос100_Ответ4. Единственное условие — правильное именование элементов на форме.

Решение 3. Архитектурный стандарт: Таблица значений на форме

Давайте сделаем еще один шаг вперед в сторону профессиональной разработки. Создавать на форме сотни реквизитов под каждый вопрос (даже программно) — это не самый эффективный архитектурный подход. Что делать, если сегодня у вас 10 вопросов, а завтра их станет 150? Перерисовывать форму в конфигураторе?

Гораздо правильнее использовать Табличную часть или Таблицу значений, выведенную на форму. Давайте проанализируем этот сценарий:

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

При таком подходе вам вообще не придется заботиться о доступности элементов. Физически в колонке ВыбранныйОтвет для каждой строки (вопроса) может храниться только одно конкретное значение. Выбор нового значения из выпадающего списка автоматически перезапишет старое. Программа легко масштабируется на тысячи вопросов без изменения структуры формы и написания сложного кода логики.

Решение 4. Динамическое создание элементов на сервере

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

Давайте посмотрим на пример реализации, когда данные по вопросам считываются из базы данных, а интерфейс подстраивается под них "на лету":


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    
    // Получим список вопросов (в реальной задаче это будет запрос к справочнику или регистру)
    СписокВопросов = ПолучитьСписокВопросовИзБазы();
    
    ДобавляемыеРеквизиты = Новый Массив;
    
    // Шаг 1. Создаем реквизиты формы динамически под каждый вопрос
    Для Каждого СтрокаВопроса Из СписокВопросов Цикл
        ИмяРеквизита = "Вопрос_" + СтрокаВопроса.Код;
        НовыйРеквизит = Новый РеквизитФормы(ИмяРеквизита, Новый ОписаниеТипов("Число"), , СтрокаВопроса.Наименование);
        ДобавляемыеРеквизиты.Добавить(НовыйРеквизит);
    КонецЦикла;
    
    // Применяем изменения структуры формы
    ИзменитьРеквизиты(ДобавляемыеРеквизиты);
    
    // Шаг 2. Отрисовываем элементы управления на форме
    Для Каждого СтрокаВопроса Из СписокВопросов Цикл
        
        ИмяРеквизита = "Вопрос_" + СтрокаВопроса.Код;
        
        // Создаем группу для визуального отделения одного вопроса от другого
        ГруппаВопроса = Элементы.Добавить("Группа_" + ИмяРеквизита, Тип("ГруппаФормы"), ЭтаФорма);
        ГруппаВопроса.Вид = ВидГруппыФормы.ОбычнаяГруппа;
        ГруппаВопроса.Заголовок = СтрокаВопроса.ТекстВопроса;
        
        // Создаем переключатель для выбора ответа
        НовыйЭлемент = Элементы.Добавить("Поле_" + ИмяРеквизита, Тип("ПолеФормы"), ГруппаВопроса);
        НовыйЭлемент.Вид = ВидПоляФормы.ПолеПереключателя;
        НовыйЭлемент.ПутьКДанным = ИмяРеквизита;
        
        // Наполняем варианты ответов из базы данных
        Для Каждого ВариантОтвета Из СтрокаВопроса.ВариантыОтветов Цикл
            НовыйЭлемент.СписокВыбора.Добавить(ВариантОтвета.Значение, ВариантОтвета.Представление);
        КонецЦикла;
        
    КонецЦикла;
    
КонецПроцедуры

Используя этот метод, вы получаете абсолютно гибкое и масштабируемое решение. Сколько бы вопросов и вариантов ответов ни добавил пользователь в базу данных, форма всегда будет генерироваться корректно, аккуратно и работать без единой строчки кода интерактивной обработки на клиенте.

Решение 5. Декларативное управление через Условное оформление

Если вам все же необходимо управлять доступностью полей программно, но вы хотите избежать написания кода в процедурах ПриИзменении, в управляемом приложении 1С 8.3 существует мощный механизм — Условное оформление формы.

Вы можете задать правила доступности полей декларативно. Например, сделать так, чтобы при установке флажка Ответ1 в значение Истина элементы Ответ2, Ответ3 и Ответ4 автоматически блокировались силами самой платформы.

Проанализируем пример программной настройки такого оформления в процедуре ПриОткрытии или ПриСозданииНаСервере:


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

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

Подведем итоги

Мы рассмотрели несколько эффективных способов решения проблемы раздувания кода при работе с однотипными элементами формы:

← На главную