В прошлом уроке мы научились работать с текстовым вводом от пользователя.
Давайте еще немного улучшим UX: будем закрашивать кнопку в красный цвет, если человек ответил неправильно, и в зеленый - если правильно. Для начала возьмем тот код, который мы создали в прошлом уроке, но чтобы упростить пример, уберем кнопку произвольного ответа и вернем правильный ответ в список.
Вы можете продолжить в question.tsx
, или создать новый файл.
// Import showToast action
import {showToast} from '@app/ui'
const question = {
label: 'The Capital of Great Britain',
answers: [ 'New York', 'Liverpool', 'London' ],
correct: 'London',
}
// Screen with 3 buttons and question
const questionScreen = app.screen('/', async function(ctx, req) {
return <screen title="Question">
<text class="section">{question.label}</text>
{question.answers.map( answer =>
<button
onClick={buttonHandler.apiCall({ value: answer })}
class={["secondary","section"]}
title={answer}
/>
)}
</screen>
})
// Handler for button
const buttonHandler = app.apiCall('/check', async function(ctx, req) {
return ( req.body.value === question.correct )
? showToast("Correct")
: showToast(req.body.value + ' is not correct');
})
Напомним, что делает этот код: он показывает вопрос и варианты ответа на него. При клике на правильный вариант он выдает сообщение "Correct", на неправильный - выдает сообщение об ошибке.
Итак, наша задача - сделать так чтобы при клике на кнопку ответа она меняла цвет. В классическом React-подходе это смена состояния у экрана - React меняет состояние на клиенте и применяет его тут-же.
В нашем случае важно понять одну вещь:
Если мы хотим изменить состояние экрана - мы должны выдать новый экран с сервера. Изменить состояние можно либо через изменение данных, либо просто измененив URL.
Например, можно добавить что-нибудь в URL query-сегмент - то что идет в адресе после символа вопроса.
В самом коде экрана можно посмотреть на объект req.query
. В него приходит query из HTTP-запроса. То есть если мы передадим в адресе ?q=value1
, то в коде экрана сможем получить это значение через req.query.q
.
Давайте поменяем обработчик нажатия кнопки таким образом, чтобы он возвращал нас на экран с индикацией ответа.
// Handler for button
const buttonHandler = app.apiCall('/check',async function(ctx, req){
const messageAction = ( req.body.value === question.correct )
? showToast("Correct")
: showToast(req.body.value + ' is not correct');
const redirectAction = questionScreen.navigate(
{queryParams: {'answered': req.body.value}, replace: true}
);
return [messageAction, redirectAction];
})
В этом коде мы объявляем 2 действия и возвращаем их в массиве. Приложение выполнит их последовательно: сначала покажет сообщение, а потом сделает переход.
Обратите внимание на второй аргумент в navigate
. В нем мы передаем {replace: true}, что делает замену текущего экрана, вместо открытия нового.
Теперь выведем значение answered
в экране, чтобы убедиться, что все сделали правильно.
Добавим вывод ответа под вопросом.
<text class="section">{question.label}</text>
{req.query?.answered && <text class="section">
Answered: {req.query.answered}
</text>}
Обратите внимание на конструкцию {req.query?.answered && ...}
. Она позволяет выводить тег <text>...</text>
только если req.query
существует и req.query.answered
что-то содержит.
Теперь давайте перенесем эту логику в раскрашивание кнопки.
Сейчас, кнопка каждого из вариантов ответа имеет 2 класса: {['secondary', 'section']}
. Заменим этот код так, чтобы вместо 'secondary'
была переменная, отвечающая за цвет.
{question.answers.map( answer => {
let buttonClass = "secondary"
if ( req.query && req.query.answered == answer ) {
buttonClass = answer == question.correct ?
"success" : "danger"
}
return <button
onClick={buttonHandler.apiCall({ value: answer })}
class={[ buttonClass ,"section"]}
title={answer}
/>
}
)}
Этот код выводит на экран кнопки. Та, на которую кликнул пользователь, будет окрашена: присвоен класс "success" если этот вариант ответа является правильным или класс "danger" если нет.