Форма поиска банка Primer. Часть 2. Организуем контроль правильности ввода
Продолжение серии статей, описывающих поцесс разработки управляющей формы в демо-банке Primer.
Предыдущая статья:
Прошлый раз мы создали и настроили заготовку формы для отбора записей, а также подготовили функции для формирования запроса и отображения отобранных записей.
Сегодня займемся полями, предназначенными для ввода ограничений
на возраст кандидата (txtAgeFrom
и txtAgeTo
), и научим форму отбирать
записи с учетом этих ограничений.
По нашему замыслу значениями этих полей должны быть положительные числа, на основании которых мы будем формировать поисковые ограничения на характеристику “Дата рождения” базы “Лицо”.
Чтобы обеспечить формирование корректного запроса нам нужно помочь пользователю ввести правильные значения: предупредить об ошибках и, по возможности, откорректировать их.
Контроль на этапе ввода
В первую очередь будем предупреждать пользователя о вводе некорректного значения непосредственно в процессе набора. Для этого используем событие ContentChange, которое происходит при любом изменении свойства Text поля, т.е. не только при наборе с клавиатуры, но и при вставке из буфера или присваивании нового значения скриптом.
В случае ошибки не будем отвлекать пользователями всплывающими сообщениями, а просто подкрасим значение красным.
if control.Text:match("%D") then
control.ForeColor = Color.Red
else
control.ForeColor = Color.DimGray
end
Поскольку абсолютно идентичные по содержанию обработчики нам понадобятся
для обоих полей txtAgeFrom
и txtAgeTo
(а в будущем и для поля txtExperience
),
оформим их отдельной функцией:
function AgeTypeCheck(event)
-- контроль ввода в полях возраста и стажа
local control = event.Control
if control.Text:match("%D") then
control.ForeColor = Color.Red
else
control.ForeColor = Color.DimGray
end
end
Теперь в обработчике события Load формы свяжем событие ContentChange трех полей с этой функцией:
Фрагмент функции Форма_Load:
-- Контроль ввода (стаж и возраст)
local age_ctrl = {Me.txtAgeFrom, Me.txtAgeTo, Me.txtExperience }
for _, ctrl in pairs(age_ctrl) do
ctrl.ContentChange = AgeTypeCheck
end
Будем великодушны
Людям свойственно ошибаться. они неизбежно будут вводить (вставлять из буфера обмена) в поля что-нибудь, похожее на “до 50” или “30 - 60”. Попытаемся предугадать наиболее распространенные варианты опечаток и исправить их.
Будем использовать событие TypeValidationCompleted, которое происходит при попытке выхода фокуса с элемента TextBox.
Первым делом очистим введенное значение от лишних пробелов, а также от символов до первой и после последней цифры (от 30 лет → 30).
local control = event.Control
local val = control.Text:trim()
val = val:gsub("^%D+","")
val = val:gsub("%D+$","")
Если в результате останется только строка из цифр, преобразуем ее в число и запишем как новое значение поля.
if not val:match("%D") then
control.Text = tonumber(val)
return
end
Теперь проанализируем строки вида “число-число” и попытаемся разумно их интерпретировать.
Будем исходить предположения, что границы диапазона должны попадать в интервал
14-65 лет и второе должно быть больше первого. При соблюдении этих условий
первое число следует поместить в поле txtAgeFrom
, а второе - в поле txtAgeTo
:
local min, max = 14, 65
val = val:gsub("%s+%-","-")
val = val:gsub("%-%s+","-")
if val:match("%d+%-%d+") then
local st, ed = val:match("(%d+)%-(%d+)")
local st_ed = st..ed
st = tonumber(st)
ed = tonumber(ed)
st_ed = tonumber(st_ed)
if ed > st and st >= min and st <= max and ed <= max then
Me.txtAgeFrom.Text = st
Me.txtAgeTo.Text = ed
return
end
end
Возможно, дефис или другой нецифровой символ попал в запись случайно: хотели написать “30”, а получилось “3-0” или “3щ0”.
Исключим этот символ и проверим получившееся значение на соответствие интервалу 14-65 лет.
-- проверка на случайно набранный нецифровой символ (2r5)
if val:match("%d+%D%d+") then
local st_ed = val:match("%d+%D%d+"):gsub("%D","")
st_ed = tonumber(st_ed)
if st_ed >= 14 and st_ed < 65 then
control.Text = st_ed
return
end
end
И, наконец, если ничто другое не помогло, оставим в качестве значения первую встретившуюся в записи последовательность цифр.
control.Text = val:match("%d+")
Полный листинг функции проверку и коррекцию значений в полях
txtAgeFrom
и txtAgeTo
:
function AgeValidation( event )
local min, max = 14, 65
local control = event.Control
local val = control.Text:trim()
-- очистка от нецифровых символов в начале и в конце строки
val = val:gsub("^%D+","")
val = val:gsub("%D+$","")
if not val:match("%D") then
control.Text = tonumber(val)
return
end
-- проверка на диапазон (напр., 20-50 )
val = val:gsub("%s+%-","-")
val = val:gsub("%-%s+","-")
if val:match("%d+%-%d+") then
local st, ed = val:match("(%d+)%-(%d+)")
local st_ed = st..ed
st = tonumber(st)
ed = tonumber(ed)
st_ed = tonumber(st_ed)
if ed > st and st >= min and st <= max and ed <= max then
Me.txtAgeFrom.Text = st
Me.txtAgeTo.Text = ed
return
end
end
-- проверка на случайно набранный нецифровой символ (2r5)
if val:match("%d+%D%d+") then
local st_ed = val:match("%d+%D%d+"):gsub("%D","")
st_ed = tonumber(st_ed)
if st_ed <= max then
control.Text = st_ed
return
end
end
-- последний рубеж
control.Text = val:match("%d+")
end
В результате выполнения этой функции в соответствующем поле будет записано либо целое число (положительное или равное нулю), либо пустая строка.
Для стажа (поле txtExperience
) создадим аналогичный обработчик,
но без проверки на диапазон и с другим допустимым интервалом значений (0-20 лет).
function ExperienceValidation( event )
local min, max = 0,20
-- коррекция значений в полях возраста и стажа
local control = event.Control
local val = control.Text:trim()
-- очистка от нецифровых символов в начале и в конце строки
val = val:gsub("^%D+","")
val = val:gsub("%D+$","")
if not val:match("%D") then
control.Text = tonumber(val)
return
end
-- проверка на случайно набранный нецифровой символ (2r5)
if val:match("%d+%D%d+") then
local st_ed = val:match("%d+%D%d+"):gsub("%D","")
st_ed = tonumber(st_ed)
if st_ed >= min and st_ed <= max then
control.Text = st_ed
return
end
end
-- последний рубеж
control.Text = val:match("%d+")
end
Осталось добавить обработчике события Load формы связь события TypeValidationCompleted
с соответствующими функциями:
Фрагмент функции Форма_Load:
-- Контроль ввода (стаж и возраст)
local age_ctrl = {Me.txtAgeFrom, Me.txtAgeTo, Me.txtExperience }
for _, ctrl in pairs(age_ctrl) do
ctrl.ContentChange = AgeTypeCheck
if ctrl.Name:match("^txtAge") then
ctrl.TypeValidationCompleted = AgeValidation
else
ctrl.TypeValidationCompleted = ExperienceValidation
end
end
Включаем возраст в условия отбора
Осталось доработать функцию MakeRequest, с тем, чтобы формировала запрос
вида ОТ ЛЦ01 9 БР <min_date> И 9 МР <max_date>
, где min_date и max_date
вычисляются с учетом текущей даты и значений в полях txtAgeFrom и txtAgeTo:
function MakeRequest()
-- Формирование строчной записи запроса
local req = "ОТ ЛЦ01"
conditions = {}
-- возраст
if Me.txtAgeFrom.Text ~= "" then
local cur_year = DateTime.Now.Year
local val = tonumber(Me.txtAgeFrom.Text)
table.insert(conditions,"9 МР 31.12."..(cur_year - val))
end
if Me.txtAgeTo.Text ~= "" then
local cur_year = DateTime.Now.Year
local val = tonumber(Me.txtAgeTo.Text)
table.insert(conditions,"9 БР 00.00."..(cur_year - val))
end
-- стаж
if Me.txtExperience.Text ~= "" then
conditions.experience = tonumber(Me.txtExperience.Text)
end
if #conditions > 0 then
req = req.." "..table.concat(conditions," И ")
end
return req
end
Для каждого из полей txtAgeFrom
и txtAgeTo
мы вычитаем его значение
из текущего года и формируем
поисковое ограничение для характеристики “Дата рождения”.
Для поля txtAgeFrom
как максимально возможную в данном голу дату,
а для поля txtAgeTo
- как минимально возможную.
Все формируемые условия запроса мы накапливаем в таблице conditions
,
которую следует объявить в начале модуля формы, чтобы она была доступна
и из других функций (поверьте, она нам еще понадобится).
Значение поля txtExperience
мы сохраняем в отдельном именованном поле таблицы
conditions
, но при формировании запроса его не используем.
Отбор кандидатов по стажу мы оставим напоследок: структура банка не позволяет
выполнить его обычным запросом, и для фильтрации записей по этому параметру
мы напишем отдельный код после того, как будут отработаны все остальные условия.
Дополнительные материалы
Полный текст модуля формы на текущий момент приведен здесь.
Страница создана на основе шаблона flexible-jekyll