Недавно возникла у меня задача спросить у пользователя подтверждение его действий перед запуском одной обработки. Но оказалось, что в коде уже присутствует один вопрос. С моим он никак логически не связан, разве что ответ "нет" на любой вопрос должен отменить обработку данных. В модальных окнах код был бы простым, как на схеме 1.
Однако в моём случае пришлось использовать метод "ПоказатьВопрос" и в этом случае кода стало сильно больше, как на схеме 2.
Тут я испугался, что скоро понадобится добавить третий вопрос и понял, что надо что-то делать. Понятно, что действия в блоке "Продолжение программы" нужно вынести в отдельную процедуру и вызывать её из разных мест. Но код всё еще будет путано выглядеть, да и каждый новый вопрос будет увеличивать количество строк кода, как снежный ком. Решение было очевидным - рекурсия!
Я люблю рекурсию, к сожалению она не отвечает мне взаимностью. Поэтому пришлось потратить время на отладку кода. И, дабы не терять время в будущем самому и уберечь от этого коллег, родилась эта статья.
Логика простая - сначала подготовим массив вопросов, затем рекурсивно их зададим, а в обработчике дальнейших действий у нас будет доступен наш массив вопросов и ответов на них.
На первом этапе нам нужно определить все вопросы, которые следует задать пользователю. Чаще всего это будет зависеть от каких то условий, я для тестов просто разместил на форме три булевых параметра.
Задаём массив вопросов пользователю
МассивВопросовПользователю = Новый Массив;
Если Объект.Парам1 Тогда
Стр = СоздатьВопросПользователю("ПервыйВопрос", // идентификатор вопроса, позволяет найти ответ в обработчике ответов
КодВозвратаДиалога.Да,// если ответ Да - пойдем дальше, иначе останавливаем вопросы
,
"Есть ли жизнь на марсе?",
РежимДиалогаВопрос.ДаНет,
5, // таймаут в секундах
КодВозвратаДиалога.Нет, // кнопка по умолчанию
"ВНИМАНИЕ", // заголовок
КодВозвратаДиалога.Нет // кнопка таймаута
);
МассивВопросовПользователю.Добавить(Стр);
КонецЕсли;
Если Объект.Парам2 Тогда
Стр = СоздатьВопросПользователю("ВторойВопрос",
,
КодВозвратаДиалога.Прервать,// если ответ Прервать - останавливаем вопросы
"Почём опиум для народа?",
РежимДиалогаВопрос.ПрерватьПовторитьПропустить,
0,
КодВозвратаДиалога.Прервать,
"",
КодВозвратаДиалога.Прервать
);
МассивВопросовПользователю.Добавить(Стр);
КонецЕсли;
Если Объект.Парам3 Тогда
Стр = СоздатьВопросПользователю("ТретийВопрос",
, //можно не передавать параметры, значит вопросы будут задаваться не зависимо от ответа
,
"Понравился ли вам этот код?",
РежимДиалогаВопрос.ПовторитьОтмена,
0,
КодВозвратаДиалога.Повторить,
"",
КодВозвратаДиалога.Повторить
);
МассивВопросовПользователю.Добавить(Стр);
КонецЕсли;
Функция СоздатьВопросПользователю имеет три собственных параметра: ИдентификаторВопроса по этому идентификатору можно будет узнать ответ пользователя в процедуре обработки результатов вопросов. Также есть два необязательных параметра - КнопкаПродолжения и КнопкаОстановки. Они имеют инвертированную логику. Если задан параметр КнопкаПродолжения, то дальнейшие вопросы будут задаваться только если ответ пользователя совпал с этим параметром. И наоборот, если задан параметр КнопкаОстановки, то при выборе пользователем этого варианта дальнейшие вопросы задаваться не будут. Остальные параметры совпадают с параметрами платформенной "ПоказатьВопрос" и нужны как раз для её вызова.
Далее вызываем рекурсивную процедуру ПоказатьВопросПользователю, но сначала проверим, что вопросы вообще есть и сформируем структуру дополнительных параметров:
Если МассивВопросовПользователю.Количество() > 0 Тогда
ДопПараметрыВОповещенииПоказатьВопросПользователю = Новый Структура;
ДопПараметрыВОповещенииПоказатьВопросПользователю.Вставить("МассивВопросовПользователю",МассивВопросовПользователю);
ДопПараметрыВОповещенииПоказатьВопросПользователю.Вставить("СчетчикВопросов",-1); //первый проход будет холостой и счетчик станет 0
ПоказатьВопросПользователю(Неопределено,ДопПараметрыВОповещенииПоказатьВопросПользователю);
КонецЕсли;
Собственно на этом вызов завершён, остается добавить процедуру, в которой мы получим массив структур с вопросами и ответами на них. Я для теста просто вывел их на экран, надеюсь логика понятна.
&НаКлиенте
Процедура ОбработкаОтветовПользователя(Параметры)
Для каждого элт из Параметры.МассивВопросовПользователю цикл
Сообщить(элт.ИдентификаторВопроса);
Если элт.Свойство("ОтветНаВопрос") Тогда //если вопрос не задавался, то ответа не будет
Сообщить(элт.ОтветНаВопрос); //вот тут имеем пару ИдентификаторВопроса:ответНаВопрос
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Ниже универсальный код, его можно забрать себе. Единственное, что там жестко указано - вызовы процедуры "ОбработкаОтветовПользователя". Если требуется её переименовать, не забудьте их изменить.
Функция СоздатьВопросПользователю
// Функция - Создать вопрос пользователю
//
// Параметры:
// ИдентификаторВопроса - строка - по идентификатору вопроса можно будет узнать ответ в обработчике ОбработкаОтветовПользователя
// КнопкаПродолжения - КодВозвратаДиалога - Если задано и ответ пользователя совпадает с этой кнопкой будет задан следующий вопрос (если есть, в противном случае сразу переход в ОбработкаОтветовПользователя)
// КнопкаОстановки - КодВозвратаДиалога - Если задано и ответ пользователя НЕ совпадает с этой кнопкой будет задан следующий вопрос (если есть, в противном случае сразу переход в ОбработкаОтветовПользователя)
// ТекстВопроса - - Остальные параметры совпадают с параметрами типовой процедуры ПоказатьВопрос
// Кнопки - -
// Таймаут - -
// КнопкаПоУмолчанию - -
// Заголовок - -
// КнопкаТаймаута - -
//
// Возвращаемое значение:
// Структура - структура с необходимыми данными для вопроса пользователю
//
&НаКлиенте
Функция СоздатьВопросПользователю(ИдентификаторВопроса, КнопкаПродолжения = Неопределено, КнопкаОстановки = Неопределено,
ТекстВопроса,Кнопки,Таймаут=0,КнопкаПоУмолчанию,Заголовок,КнопкаТаймаута)
Стр = Новый Структура;
Стр.Вставить("ИдентификаторВопроса",ИдентификаторВопроса);
Если КнопкаПродолжения <> Неопределено Тогда
Стр.Вставить("КнопкаПродолжения",КнопкаПродолжения);
КонецЕсли;
Если КнопкаОстановки <> Неопределено Тогда
Стр.Вставить("КнопкаОстановки",КнопкаОстановки);
КонецЕсли;
Стр.Вставить("ТекстВопроса",ТекстВопроса);
Стр.Вставить("Кнопки",Кнопки);
Стр.Вставить("Таймаут",Таймаут);
Стр.Вставить("КнопкаПоУмолчанию",КнопкаПоУмолчанию);
Стр.Вставить("Заголовок",Заголовок);
Стр.Вставить("КнопкаТаймаута",КнопкаТаймаута);
Возврат Стр;
КонецФункции
Процедура ПоказатьВопросПользователю
&НаКлиенте
Процедура ПоказатьВопросПользователю(Результат,Параметры) Экспорт
Если Параметры.СчетчикВопросов = Параметры.МассивВопросовПользователю.Количество()-1 Тогда
Параметры.МассивВопросовПользователю[Параметры.СчетчикВопросов].Вставить("ОтветНаВопрос",Результат);
ОбработкаОтветовПользователя(Параметры); //все вопросы заданы, обработаем ответы
Иначе
ПоказатьСледующийВопрос = Ложь;
//обработка ответа
Если Результат = Неопределено Тогда
ПоказатьСледующийВопрос = Истина; //в первом вызове вопрос не задавался и ответа ещё нет
Иначе
Параметры.МассивВопросовПользователю[Параметры.СчетчикВопросов].Вставить("ОтветНаВопрос",Результат);
ПораОстановиться = Ложь;
Если Параметры.МассивВопросовПользователю[Параметры.СчетчикВопросов].Свойство("КнопкаПродолжения")
И Результат <> Параметры.МассивВопросовПользователю[Параметры.СчетчикВопросов].КнопкаПродолжения Тогда
ПораОстановиться = Истина;
КонецЕсли;
Если Параметры.МассивВопросовПользователю[Параметры.СчетчикВопросов].Свойство("КнопкаОстановки")
И Результат = Параметры.МассивВопросовПользователю[Параметры.СчетчикВопросов].КнопкаОстановки Тогда
ПораОстановиться = Истина;
КонецЕсли;
Если ПораОстановиться Тогда
ОбработкаОтветовПользователя(Параметры);
Иначе
ПоказатьСледующийВопрос = Истина;
КонецЕсли;
КонецЕсли;
Если ПоказатьСледующийВопрос Тогда
Параметры.СчетчикВопросов = Параметры.СчетчикВопросов + 1;
Стр = Параметры.МассивВопросовПользователю[Параметры.СчетчикВопросов];
Оповещение = Новый ОписаниеОповещения("ПоказатьВопросПользователю",
ЭтотОбъект,Параметры);
ПоказатьВопрос(Оповещение, Стр.ТекстВопроса,Стр.Кнопки, Стр.Таймаут,
Стр.КнопкаПоУмолчанию, Стр.Заголовок, Стр.КнопкаТаймаута);
КонецЕсли;
КонецЕсли;
КонецПроцедуры
На этом всё. Надеюсь, кому-нибудь пригодится.