Now in the last lesson, we've somewhat created our front end with just HTML and CSS. 1
And also we've got our Motoko code, which is pretty much all set up. Now 2
we just need that bridge in between the two of them, which is our index.js, the JavaScript code 3
that's going to tell the frontend how it should interact with the backend. 4
So the first thing we need to do is we need to be able to expose the important parts of this Motoko 5
code to our index.js. And to do that we have to import the dbank module. 6
So what I'm going to say is import and then we're going to add the curly braces. Inside here 7
we're going to add the name of our module, which is our Dbank, and then we're going to specify where 8
this should be imported from. 9
Now, I'm going to use a relative path. 10
If you're not sure about relative and absolute paths, head back a few modules. 11
We talked about this in more detail. But we're going to go into the declarations folder which is right 12
here, and we're going to tap into the dbank folder. 13
And the files inside the declarations basically just exposes our Motoko code to our JavaScript in a 14
way that our JavaScript code is able to understand and is able to access and call these functions. 15
So it kind of just functions as a bridge between the two languages. 16
And then we're able to call and use these functions like checkBalance, compound, topUp and withdrawl. 17
So what do we want to address first on our frontend? 18
Well, I'd love to be able to update this line so that it's not just the placeholder text, to actually 19
show the current balance. 20
To do that, I'm going to tap into this query function, which is the checkBalance and this should 21
be able to happen pretty quickly and it should be able to return us the current value. 22
So I want this balance to be updated every time this page loads. 23
That means we're going to have to add in an event listener in JavaScript to listen for when the page 24
has loaded. 25
So let's tap into our window object, add event listener and the name of the event listener is load. 26
And this is basically going to get called when all the code on the page has finished loading up. 27
And when it has finished, then it's going to call a callback function and this function is going to 28
do something. 29
So let's just go ahead and do a console.log. 30
All right. 31
So that's a pretty simple bit of JavaScript code. 32
Let's go ahead and test it out. 33
So if we bring up our console, and make sure you're in the console tab and then let's go ahead and reload 34
the page, you can see it says finished loading. 35
So that's working and that's great. 36
Now let's go ahead and comment that out. 37
Now, instead of finished loading, what I want to do is I want to be able to update this span here. 38
So instead of having the value of 234, I want to change it to say, well, whatever 39
is the value of our currentValue here. 40
So to do that, let's go ahead and create a constant and I'll call that the currentAmount and we'll 41
set that equal to the dbank.checkBalance result. 42
So basically once we've checked the balance, we're going to assign it to this current amount and then 43
we're going to use the document.getElementById and then use the getElementById to grab hold of 44
that span, which has the ID of value, which you can see right here. 45
And then once we got hold of it, then we can change it's inner text to be equal to whatever is the current 46
Amount. 47
So let's go ahead and hit save and test this out. 48
And you can see once you refresh, that we've got something called an object promise here. 49
So what's happening here? 50
It's obviously not doing what we want it to. 51
Well, the reason is because the current value is returned asynchronously. 52
So that means we're not actually waiting for this result to come back. 53
And instead there is a promised object which can return at any point depending on how long it takes 54
to query the blockchain. 55
So what we have to do instead here is we have to turn this also into an asynchronous function so that 56
we can adapt, and we have to make this method call have the await keyword in front so that we basically 57
wait for the result to come back and then asynchronously update our element with the current amount 58
once it does return. 59
So let's hit save, let's refresh and you can see our current balance now shows up. 60
Now this is quite a few digits after the decimal place and it kind of is a bit too much and it clutters 61
up the screen. 62
What we want is to reduce it or round it to have only two decimal place accuracy. 63
If you can't remember how to do this using JavaScript, then it might be a good time to have a look 64
at the documentation or have a quick Google search on StackOverflow. 65
But I'm going to set this as a small challenge for you. 66
See if you can figure out how to modify the code here so that instead of having all of these decimal 67
places afterwards, we only have two decimal place accuracy. 68
Pause the video and give that challenge to go. 69
With JavaScript, we have this very helpful function called Math.round, and that means we can round 70
our current amount. 71
Now if we want to round it two decimal places, the easiest way is to simply multiply the current amount 72
by 100, round it so that there's no decimal places, 73
and then afterwards, once that's done, just simply divide it by 100 again. And now you can see it 74
update and we now have two decimal place accuracy. 75
Perfect. 76
Exactly what we want. 77
Now that we've gotten this past sorted, the next thing to do is to listen for when the user finalizes 78
their transaction 79
after typing in either an amount of top up or an amount to withdraw. And then trigger, the corresponding 80
top up or withdraw, bit of a typo here, functions inside our main.mo. 81
So let's go back to our index.js and we need to add an event listener to this form here that we've 82
got in our HTML. 83
The form has a input at the very end which has the type of submit so we can actually listen for that 84
event on this form. 85
Let's go ahead and get hold of the form so we can say document.querySelector, and this allows 86
us to select a an item inside our DOM. 87
And in this case, I'm just going to get a hold of the form. 88
So remember that with query selector you can use the pound sign for IDs, the dot for class, and if 89
you're trying to query for a HTML element, then all you have to do is just give its name. 90
So it's the same as how CSS selection works. 91
Once we've gotten hold of our form, let's go ahead and call add event listener so that we listen on 92
this element, and then we're going to listen for the event which is called submit. 93
Basically, this is going to be triggered when that submit button here gets tapped. 94
And once that happens, then we have to describe what should happen. 95
So we're going to create another async function, and this function is going to let us know what event 96
triggered it. 97
And inside this callback, let's first start out with a console.log so that we can just make sure it's 98
actually getting triggered when the submit button is pressed. 99
So now let's hit save and then we can go back and add a amount to top up or an amount to withdraw and 100
click on a button and you can see it says submitted, although sometimes it shows submitted very quickly 101
and then it kind of just disappears. 102
So that is because with forms there's a default action, which is to actually clear the form and submit 103
the values. 104
Now, if we want to remove that default action, then we can actually get hold of this event, 105
so the event of submit, and then we can call preventDefault on that. 106
So if we save again and let's go ahead and do that, you can see now it doesn't actually clear any of 107
our fields and we always see this submitted down here and it doesn't reload the page and get rid of 108
it. 109
Let's comment this console.log out. 110
The next step is to actually get hold of what the user typed in here or in here. 111
Let's create a new const and I'll call that inputAmount. 112
And I'll set it equal to whatever is inside that field, which is going to be document.getElement 113
ById. 114
And the id of it is input amount. 115
And then let's get hold of its value. 116
Now let's do the same thing to the withdraw amount 117
but for simplicity's sake, I'm just going to call it the output amount because I seem to keep making 118
typos on the withdraw. 119
So again, getElementById 120
and I'm going to 121
go ahead and copy this id over so I don't make any typos in it. 122
And then let's get a hold of its value as well. 123
So now that we've got what the user typed in to the input amount or the output amount, the next step 124
is to actually use this to top up or withdraw from our funds in the main.mo. 125
So we basically want to trigger this function first, 126
right? Now, let's try it out. 127
Let's simply do what we did before, which is to use the await keyword and then we tap into the Dbank 128
and we call this topUp function. 129
This topUp function expects an input and the amount is going to be the inputAmount, whatever the 130
user typed in here. 131
Now if we go ahead and hit save and try to run this code, you'll see that we get an error and the error 132
says invalid float64 argument 133
and then it points to this number 10 that we added in here. 134
So what's happening? 135
Well, do you remember I said when we imported our Dbank did from the declarations, 136
it basically tells all JavaScript file which functions are available in our main.mo and how to call 137
it. 138
Now, in this case, the topUp function expects an input that has a data type of a 64 bit floating 139
point number, because that's what we've got right here. 140
But at the moment, the values that come out of an input with type number is by default a integer. 141
So we have to convert this into a floating point number. 142
And you can see that actually for these inputs, I've already added the step amount, which means that 143
you can actually enter a value with up to two decimal places of accuracy. 144
Now you can add more numbers afterwards in the input, but once it actually comes through, the value 145
will just be accurate to two decimal places. 146
So this input actually allows for floating-point numbers to be entered, but in order to convert it 147
into an actual floating-point number, we have to use the method parseFloat and it will parse this value 148
into a floating-point number. 149
And while we're at it, let's go ahead and do the same for the output value as well. 150
So now if we hit save and we try this out again, you can see that we no longer get any errors. 151
Click finalise transaction, 152
looks like nothing's happening, but all errors have gone away and it's actually behind the scenes done 153
what we want. 154
So in order to view what we've got, well, we actually have to do what is in these lines of code, 155
right? 156
We have to check balance and update that span again. 157
So for now, I'm just going to copy and paste those two lines right here, 158
hit save, 159
go back to my website and let's try it out. 160
Let's top it up by $10. 161
So the moment we're on 1446, click finalize transaction, and that jumps to 162
1456. 163
So perfect. 164
It's actually working. 165
Now, one of the problems you might notice with our current code is when we type an amount to top up 166
and we click on this button, it looks like as if nothing is happening, but then it actually does work 167
behind the scenes. 168
So remember what we said about these update functions in the ICP taking a little bit of time. 169
So it can take anywhere between 2 to 3 seconds. 170
And we don't want the user to look at this frontend and think that nothing's happening because they'll 171
try and click this button lots of times, which is going to really mess up our backend. 172
So let's give them some clues. 173
So one of the things that we can do is as soon as they click the button submit, we can actually disable 174
that button. So we can tap into our button. 175
So let's create a new constant for it and we'll get hold of the event.target and we'll add the 176
querySelector. 177
So basically this is going to get hold of the form that we've got, that we're listening to, when it clicks 178
submit 179
and then we're going to query for the selector, which is our submit button. 180
And our Submit button handily has an id called a submit btn. 181
So let's go ahead and add that selector in there with the pound sign to select it. 182
And this is going to be our button. 183
So then as soon as basically the user types in the input, we can tap into our button and basically 184
say setAttribute 185
and we can set the disabled attribute, which you have, to true. 186
And then once we've done the top up and we've gotten hold of the actual new balance, then we can go 187
ahead and tap into that button again. 188
And instead of using setAttribute, we can use the removeAttribute 189
function. And we're going to again tap into the disabled attribute and we're basically just going to 190
remove it, because if you try to set the attribute to false, it's actually not going to work. 191
And this is because an element becomes disabled if the disabled attribute is set, so it doesn't have 192
to matter what its value is. 193
It could be true or it could be false. 194
It does the same thing. 195
The element basically just gets disabled, so we have to remove it completely 196
instead. Let's hit save on our code. 197
Let's try it again. 198
Refresh, add our $10, click finalize transaction, and you can see I've added some separate CSS to basically 199
tell the user when it is that you can't interact with it. 200
And as soon as this updates and we're back to being able to top up or withdraw, the button comes 201
back on line. 202
Now one last thing that would be nice is to actually remove this value once it's actually done right? 203
So to do that, we can tap into our document.getElementById, and then grab hold of our 204
input amount, which is the first input. And then we can set the value to basically just an empty 205
string. 206
So now if we try this again, when I click top up, my button gets disabled and then once it's back 207
on line, the input gets cleared as well. 208
So we're ready for the next step. 209
So I think in terms of user experience-wise, this is a lot better than what we had before. 210
So now that we've done the top up part, the natural thing that comes next is of course the withdraw. 211
Now there's just one problem that we have to tackle before we do that. 212
How does the code know that we're trying to top up or we're trying to withdraw? 213
Because right now the code is going to be carried out every single time the submit button gets pressed 214
on the form. 215
So if we leave this empty and we don't type anything in there and we click finalize transaction, you 216
can see that it not only messes with our current balance, but in our debug we can see that it's printing 217
nan, which comes from this debug print showing us the current value. 218
So what's actually going on here? 219
If we don't actually add any value to an input, it still has a value. 220
It's not a number value. 221
And if we pass that around to our backend, then it's going to mess everything up. 222
So we need to basically add some sort of condition, 223
right? 224
We need to make sure that we only top up when the user has typed something in here and we only withdraw 225
when the users type something in here. 226
So we can check that by using an if statement. 227
We can say if the document. 228
getElementById 229
nd we type in the id of that inputAmount, 230
so remember it's the input and it has an id of inputAmount. 231
Put that in here and then we get hold of its value and then we check the length of that value because 232
if it's an nan, the length will be equal to zero. 233
But if it so happens that in this case it's not equal to zero, then that means the user has basically 234
typed something in that field. 235
So then we can safely call our topUp function. 236
So now if we hit save and we leave this empty and we click finalize transaction, then we're not going 237
to have any problems. 238
Now in the process of showing you the problems around input validation, I've actually screwed up the data stored 239
on my canister. 240
So what I'm going to do is reset the data on my back end to how we had it in the beginning. 241
CurrentValue should equal 300. 242
So let's go ahead and uncomment 243
these two lines of code, reset everything to the beginning, and then let's go ahead and add a debug print 244
statement down here just to make sure that the current value is in fact being reset 245
because with the orthogonal persistance, sometimes if there isn't enough of a change in your code and 246
you hit deploy, then it doesn't actually update these stable variables. 247
So I'm going to save this file and then I'm going to end all of these processes with a control+ 248
c. So this is the npm, this is the simulated ICP. 249
So now that both of them are ended, let's spin everything up again. 250
So dfx start over here and then over here we'll do the dfx deploy to make sure that this new code 251
gets updated and we reset the current value to 300. 252
And then once all of that's done, we can again use our npm start to spin up our server. 253
And now if we come back, you can see our current balance is being set back to $300. And now if we try 254
the same thing again, so leaving nothing inside the amount to top up box, click on finalize transaction, 255
you can see that instead of getting nan, it actually just doesn't trigger that line which breaks 256
our code, which is the top up by some sort of non value because the length is basically equal to zero. 257
So now that we figured out how to do this part and making sure that it actually works, then it's time 258
for you to create the other part, which is the withdrawal component. 259
So see if you can get the outputAmount that's typed in to here to be subtracted from the currentBalance 260
using the function that we've got in here, which is called withdraw. 261
Now, it's really easy to make typos with this word, and I've done it so many times, so just be careful 262
with it. 263
And whenever you need an ID or function name, it's actually easier to just copy and paste it. That way 264
you don't make a lot of typos like I do, and you don't have weird errors that you have to debug. 265
So pause the video and make the withdrawal component work. 266
All right. 267
So again, we're going to need an if statement because these are two completely separate conditions. 268
I'm just going to split out another if statement rather use an elseif because the logic seems a bit clearer 269
to me, but it doesn't matter if you use an elseif as long as you didn't use an else, because that's 270
only going to check that the inputAmount is in fact equal to zero. 271
So in this case, we're going to tap into the document.getElementById and then the ID in this 272
case is equal to withdrawal-amount so 273
I'm just going to copy and paste that in here, get hold of its value.length and check that it's 274
not equal to zero. 275
So make sure that they typed something in there. And then we're again going to use the await keyword 276
to call our withdraw function, and we're going to add the output amount as the input to this function. 277
Now once that's done, our code is already going to get hold of the new balance and update that span. 278
So the final thing we have to do is just to clear that input. 279
So document.getElementById, and then again put the withdraw amount input, set its value back to 280
an empty string. 281
So now let's test it out. 282
We can top up by $10, and we can withdraw by $10 and it's all working as expected. 283
Now, the final thing I want to add is to compound my value by the interest. 284
So we'll just tap into that compound function down here after either this if statement carries out or 285
this on. 286
But either way, once the user adds some money or takes some money away, we're going to compound the 287
value. 288
And if we go back over here, hit refresh, 289
now, you'll see that every time I top up, it's going to compound. 290
And because a lot of time has passed, the current balance is absolutely massive, which means we can 291
go back over here and reset the current value and startTime. 292
So to force this to reset, we have to first, if your code in main.mo has the currentValue and start 293
time not commented out, 294
you'll have to comment it out and then run deploy. And once that's been deployed and updated, then 295
you can come back and uncomment these two lines of code, 296
hit save and run dfx deploy again just so that we force this to be reset to 300. 297
And you can see in the print statement, we're back on $300. 298
So now let's go back to our front end. 299
And if we top up by 10, you can see the current balance will be slightly more than 310. 300
And if we withdraw by 10, again, you can see that we're adding a little bit of compound interest 301
each time. 302
So that's pretty neat. 303
Now, one last thing before we finish off this index.js is it would be nice to be able to take 304
out this repeated line of code because it's right here and it's also at the top here. 305
So I'm just going to create a async function. 306
And we'll just call it update. 307
So now we can simply just call update right here 308
and also up here we can delete these lines of code and instead just call update as well. 309
Cool. 310
And just to check and make sure that it still works, you can see the amount being added and also a 311
whole bunch more from the interest. 312
So our current balances growing really quickly. 313
Now, in reality, of course, it would be pretty crazy to have a 1% interest every second. 314
But because we're working with this over a short period of time, I wanted you to be able to see it 315
easily. 316
So there you have it. 317
We finished hooking up our ICP back end with our JavaScript frontend, and we've managed to get everything 318
that we've implemented in Motoko to be accessible to the user through the frontend with HTML, CSS 319
and JavaScript. 320
Now there were quite a few points in this lesson where potential errors could have occurred. 321
So if you head over to the next lesson, I've pointed out some of these potential issues. 322
But if everything went fine, then feel free to skip the next lesson 323
and I will see you back here where we learn how to deploy this onto the live internet computer blockchain.