Creating an Action
So, we have learned how to create screens and navigate between them. Now let's try to create code that is not represented as blocks on the screen but can be invoked from it.
For example, let's take the simplest case: we will ask the user a question with three answer options. Depending on the user's action, the correct answer will show a message indicating they are right, while the others will indicate they are wrong.
Let's create a file called question.tsx
, enter the initial code, and run it.
// Import showToast action
import {showToast} from '@app/ui'
// Screen with 3 buttons and question
const questionScreen = app.screen('/', async function(ctx, req) {
return <screen title="Question">
<text class="section">The Capital of Great Britain</text>
<button onClick={showToast('Click')} class={["secondary","section"]} title="New York"/>
<button onClick={showToast('Click')} class={["secondary","section"]} title="Liverpool"/>
<button onClick={showToast('Click')} class={["secondary","section"]} title="London"/>
</screen>
})
We have three buttons on the screen. Each button has two classes - secondary and section. The secondary class is used to make the button gray, while the section class adds standard padding. Alternatively, you could use a wrapper block section
to add such padding to a group of elements. All three buttons perform the same action - displaying a message on the screen. Let's create another action and write its code below the screen code. For this, we will use the app.apiCall
method.
// Handler for button
const buttonHandler = app.apiCall('/check', async function(ctx, req) {
return showToast('Clicked');
})
This code in the question.tsx
file binds the action handler to the path template /check
. The handler will respond to POST requests at the address question~check
. To invoke this action when a button is clicked, the corresponding call must be initiated in onClick
. Let's do this for the first button "New York".
<button
onClick={buttonHandler.apiCall({ value: 'New York' })}
class={["secondary","section"]}
>
New York
</button>
<!-- Note how in this code we use ctx.router.apiCall
to form a request with parameters. This is a request to invoke the handler previously bound to the path /check
using the app.apiCall
method. Thus, the ctx.router.apiCall
method should be viewed as a mirror of the app.apiCall
method.
Parameters are specified as the second argument. They are formatted as an object with names and values of the parameters.
To get the parameter value in the handler, we use the second argument of the callback function req
, which contains the request parameters. In turn, req.body
contains all the parameters we passed when calling ctx.router.apiCall
. -->
Let's replace the static code with the parameter value from the request:
// Handler for button
const buttonHandler = app.apiCall('/check', async function(ctx, req) {
return showToast('You clicked: ' + req.body.value);
})
Now let's slightly modify the code so that a message "Correct" is displayed for the correct answer, and a different message for incorrect answers.
// Handler for button
const buttonHandler = app.apiCall('/check', async function(ctx, req) {
if ( req.body.value === 'London' ) {
return showToast("Correct")
}
else {
return showToast(req.body.value + ' is not correct');
}
})
Let's apply actions to all three buttons and check how it works.
const questionScreen = app.screen('/', async function(ctx, req) {
return <screen title="Question">
<text class="section">The Capital of Great Britain</text>
<button onClick={buttonHandler.apiCall({ value: 'New York'})} class={["secondary","section"]} title="New York"/>
<button onClick={buttonHandler.apiCall({ value: 'Liverpool'})} class={["secondary","section"]} title="Liverpool"/>
<button onClick={buttonHandler.apiCall({ value: 'London'})} class={["secondary","section"]} title="London"/>
</screen>
})
We see that there is a lot of repetitive code. To avoid this, we will separate data from presentation and modify the code to eliminate repetitions. At the same time, we will rewrite the validation code to make it shorter.