Afrikaans
Akan
Albanian
Amharic
Arabic
Armenian
Azerbaijani
Basque
Belarusian
Bemba
Bengali
Bihari
Bosnian
Breton
Bulgarian
Cambodian
Catalan
Cebuano
Cherokee
Chichewa
Chinese (Simplified)
Chinese (Traditional)
Corsican
Croatian
Czech
Danish
Dutch
English
Esperanto
Estonian
Ewe
Faroese
Filipino
Finnish
French
Frisian
Ga
Galician
Georgian
German
Greek
Guarani
Gujarati
Haitian Creole
Hausa
Hawaiian
Hebrew
Hindi
Hmong
Hungarian
Icelandic
Igbo
Indonesian
Interlingua
Irish
Italian
Japanese
Javanese
Kannada
Kazakh
Kinyarwanda
Kirundi
Kongo
Korean
Krio (Sierra Leone)
Kurdish
Kurdish (Soranรฎ)
Kyrgyz
Laothian
Latin
Latvian
Lingala
Lithuanian
Lozi
Luganda
Luo
Luxembourgish
Macedonian
Malagasy
Malay
Malayalam
Maltese
Maori
Marathi
Mauritian Creole
Moldavian
Mongolian
Myanmar (Burmese)
Montenegrin
Nepali
Nigerian Pidgin
Northern Sotho
Norwegian
Norwegian (Nynorsk)
Occitan
Oriya
Oromo
Pashto
Polish
Portuguese (Brazil)
Portuguese (Portugal)
Punjabi
Quechua
Romanian
Romansh
Runyakitara
Russian
Samoan
Scots Gaelic
Serbian
Serbo-Croatian
Sesotho
Setswana
Seychellois Creole
Shona
Sindhi
Sinhalese
Slovak
Slovenian
Somali
Spanish
Spanish (Latin American)
Sundanese
Swahili
Swedish
Tajik
Tamil
Tatar
Telugu
Thai
Tigrinya
Tonga
Tshiluba
Tumbuka
Turkish
Turkmen
Twi
Uighur
Ukrainian
Urdu
Uzbek
Vietnamese
Welsh
Wolof
Xhosa
Yiddish
Yoruba
Zulu
1 1
Let's now start implementing the next feature 2
2
of our application, 3
3
which is going to be the search functionality. 4
4
And let's start by taking a look and or flowchart here. 5
5
And so, again, 6
6
we already have this part here basically now implemented, 7
7
and now, we are going to work basically first on this part. 8
8
So handling the event of a user 9
9
searching for recipes, 10
10
then loading the search results and rendering them. 11
11
So that's going to be this lecture and the next one. 12
12
And then afterwards, 13
13
we are going to worry about the pagination. 14
14
So again, we will now focus on these three parts. 15
15
And so for that, 16
16
we need to now implement the model, the view 17
17
and the controller that binds everything together. 18
18
And usually, I find it easiest to start with the data. 19
19
So basically with the model, 20
20
which means in this case, 21
21
starting to make some API calls 22
22
in order to load some search results. 23
23
And then, once we have that part working, 24
24
we can then render them on the screen 25
25
and also care about the actual event. 26
26
Okay, so let's actually leave that open here at the end 27
27
and go to our model here, 28
28
and start implementing the search functionality. 29
29
So, just like before, 30
30
we are going to create a function 31
31
that will be exported 32
32
so that it can be used 33
33
by the controller. 34
34
And this one, 35
35
I'm gonna call it loadSearchResults. 36
36
And so, since this one will be performing AJAX calls 37
37
it's going to be an async function. 38
38
Now, again dysfunction here 39
39
is going to be called by the controller. 40
40
And so, it's going to be the controller 41
41
who will tell this function 42
42
what it would actually search for. 43
43
So basically, it will pass in a query 44
44
like string, which we can then 45
45
pluck into our API call, now, right? 46
46
And here we are going to need a try catch block, 47
47
and here in the catch block, 48
48
all we will do is to, 49
49
basically do the same as here. 50
50
So, all we're really interested in here 51
51
is to throw the error again 52
52
so that eventually, 53
53
we will be able to use it in the controller. 54
54
So just like before 55
55
in the last lecture, 56
56
where we threw with the error here again. 57
57
So that part is easy, 58
58
but now let's actually implement the functionality. 59
59
And to do that, 60
60
let's go back to our documentations page. 61
61
And so, this is how we get all recipes. 62
62
So making a get request to this path, 63
63
and then, with the search parameter, 64
64
and the way we add a parameter, 65
65
and I think I explained this before 66
66
is with the question mark, 67
67
and then search equal, 68
68
and then, pizza here in our case 69
69
is actually the search query. 70
70
And again, for now, 71
71
don't worry about the key. 72
72
And we can actually already see 73
73
what the results will look like here for pizza. 74
74
And so, basically, 75
75
this is the URL 76
76
that we need to call in our AJAX request. 77
77
So let me actually copy it 78
78
and let's go back. 79
79
And so now, 80
80
we can, just like before, 81
81
use the get JSON method. 82
82
And so, that will then fetch the data 83
83
and also convert it to JSON 84
84
and create an error if something went wrong. 85
85
So get JSON returns a promise. 86
86
And so we await that here 87
87
and store it into data, 88
88
just like always. 89
89
So get JSON 90
90
and then the URL, 91
91
and we need a template literal. 92
92
And so, yeah now we just have to replace everything 93
93
to make this work. 94
94
And the first thing 95
95
is to actually take this here again out, 96
96
and replace it with this API URL 97
97
that we already have in our configuration file, right? 98
98
So API URL, 99
99
and I think 100
100
that actually already includes the recipes. 101
101
Yup, so it's everything. 102
102
And usually, actually a URL ends with a slash, 103
103
so it should be like this. 104
104
And so here, let's take that out, 105
105
actually, and then here as well. 106
106
Then here, question mark search 107
107
and then this one is going to be our query. 108
108
Now, right? 109
109
And so, we can then 110
110
already log that data 111
111
to the console, 112
112
to take a look at it 113
113
before we then manipulate it a little bit. 114
114
And so right now, 115
115
this would already be working. 116
116
And so let's, 117
117
before we actually write any controller or any view, 118
118
or we can already call that function here. 119
119
So load search results, 120
120
and then let's try it, 121
121
has always with pizza 122
122
and see what we get. 123
123
So that's right here. 124
124
And indeed, this is the result that we get here. 125
125
So it's coming from model at line 35. 126
126
So that's where we were. 127
127
And so here, 128
128
is the data that we get back. 129
129
So we have an object 130
130
which has a data property, 131
131
which then has a recipe's property, 132
132
which is this array. 133
133
And then, each array contains 134
134
all of this data 135
135
about each of the recipes. 136
136
So, again, this object here is called data 137
137
then in there, there is data again, 138
138
and then in there, there is recipes, 139
139
which is then, 140
140
the array itself 141
141
where each of the elements 142
142
is an object 143
143
with only some of the data 144
144
about the recipe. 145
145
So you see, it's not the complete recipe object, 146
146
it's only some of the data 147
147
so that we can actually display it 148
148
then here in the search results. 149
149
So without the unnecessary data 150
150
like ingredients and all that. 151
151
All right, so let's now take this data 152
152
and store it into our state. 153
153
And also, we will want to change, 154
154
again, this one here, 155
155
so to image only. 156
156
And so, we will basically create some new objects 157
157
based on this data that we receive here. 158
158
All right. 159
159
So, we get data.data.recipes. 160
160
So this is the array. 161
161
So this is the array of all the objects. 162
162
And now we want to create a new array 163
163
which contains the new objects 164
164
where the property names are different. 165
165
So, let's map over this, 166
166
and each element 167
167
is going to be a recipe. 168
168
And then here, we will simply return a new object. 169
169
That's actually copied a code from here 170
170
because they are the same, 171
171
at least some of them. 172
172
So taking all this, 173
173
int here now, this is only called a rec. 174
174
Okay, and also we don't have the source URL, 175
175
we only have ID, title, publisher and image URL, okay. 176
176
So this will return a new array with the new objects. 177
177
And so now, 178
178
where should we store that? 179
179
Well, as you can guess, 180
180
we will put it also in our state. 181
181
So again, the state contains all the data that we need 182
182
in order to build our application. 183
183
So let's call this one search 184
184
and all the data 185
185
about the application 186
186
that I just mentioned 187
187
might as well 188
188
also include the search query itself. 189
189
So let's create a new object here 190
190
and then specify the query, 191
191
which we start as an empty string. 192
192
And then, the results, let's say, 193
193
results, and will start 194
194
as an empty array. 195
195
So, basically the state 196
196
should contain all the data 197
197
really about the application. 198
198
Now, in this case, 199
199
we might not even need the query for now, 200
200
but it's still a good idea 201
201
to already store it here in the state, 202
202
because maybe one day 203
203
we will need it. 204
204
And then the data's already there for us. 205
205
For example, we might want to add some analytics 206
206
in the future 207
207
to know which queries are made the most. 208
208
And so by then, 209
209
yeah, as I said, 210
210
that would become useful. 211
211
Okay. 212
212
But anyway, let's now store this data 213
213
in state.search.results, right. 214
214
And here at the beginning, 215
215
the very first thing 216
216
that we're going to do 217
217
is to state.search.query 218
218
equal to query. 219
219
All right. 220
220
And now, let's quickly 221
221
just to see if everything worked, 222
222
log data to the console. 223
223
So state.search.results. 224
224
And here we are. 225
225
So that is exactly the kind of array that we had before. 226
226
And now the image URL we had before 227
227
is indeed called image. 228
228
And so everything has the same format as before. 229
229
So, we needed to do that 230
230
because here the image is also called image. 231
231
And of course, we want everything to have the same names 232
232
throughout the entire application, all right. 233
233
So let's get rid of this 234
234
because of course, 235
235
we want to call this function in a controller. 236
236
And so let's go there 237
237
to the controller 238
238
and basically create a new function 239
239
which is going to be like the new controller. 240
240
So const controlSearchResults. 241
241
And so this one 242
242
we'll call the asynchronous function loadSearchResults. 243
243
And so this one, of course 244
244
then also need to be an asynchronous function itself. 245
245
And so, as always try catch, 246
246
and here for now, let's simply log the error 247
247
and we will then fix that later. 248
248
So, here is where we actually 249
249
will want to call the loadSearchResults that we just built. 250
250
So that is in model.loadSearchResults. 251
251
And here we then, 252
252
we'll need to pass in the query, 253
253
but for now, let's just do that again manually. 254
254
And of course, 255
255
we need to await this, 256
256
but we do not store any results anywhere 257
257
because, just like the loadRecipe function, 258
258
this one also doesn't return anything. 259
259
All it does 260
260
is to manipulate the state. 261
261
And so, then we should be able to do this console.log 262
262
in the controller 263
263
and still get access to the results, right? 264
264
So, let's see. 265
265
Now, of course, we first have to also call this function. 266
266
And again, later, 267
267
we will, of course hook this us up to an event list 268
268
and our material, let's just 269
269
call it right away. 270
270
And now we get status not defined, all right. 271
271
And that is because, 272
272
of course it needs to be model.state.search.results. 273
273
So you see that this is becoming a pretty long chain here 274
274
but nevermind, that's not a problem at all, 275
275
because we know exactly what all of this is. 276
276
And so, now indeed 277
277
we get back our 59 search results for pizza. 278
278
But now, instead of only searching for pizza, 279
279
we actually want to get the query right here 280
280
from this input field, right? 281
281
And we also want to start this whole process 282
282
of loading the search results. 283
283
So we only want that to happen 284
284
when this button here is clicked. 285
285
And so, now let's basically create a view 286
286
only for this search part here. 287
287
So right here, 288
288
we will have a view 289
289
for this year. 290
290
So for the search field, 291
291
and for the button, 292
292
and then a different view 293
293
which will actually render the results. 294
294
So, it's a good idea 295
295
to keep every view really focused. 296
296
And so, it's best 297
297
to just do one for this part, 298
298
and then one for displaying the results, 299
299
also because in the user interface, 300
300
they're actually in kind of different places also. 301
301
So it makes sense to have a different view. 302
302
Now this view here 303
303
is of course 304
304
not going to render anything, 305
305
but what it will do 306
306
is to basically give us the content 307
307
of this input field. 308
308
So getting that input data 309
309
is of course 310
310
something that has to do with a DOM. 311
311
And so, it should be in a view 312
312
and not in a controller, right? 313
313
So of course, 314
314
if we wanted to be lazy, 315
315
we could go ahead 316
316
and select the element that we want. 317
317
So it doesn't matter. 318
318
And then from there, 319
319
we would simply get the value 320
320
and then use that as our query. 321
321
So again, we could do that here, 322
322
but this is clearly about the DOM. 323
323
And so, in order to follow our architecture, 324
324
we will create a new view right now. 325
325
And this one is going to be called the searchView.js. 326
326
And just like before, 327
327
here, we will create a class called searchView 328
328
and we will then not export that class 329
329
but export an instance, 330
330
so an object that was created by this class. 331
331
So export default new SearchView, 332
332
and without passing in any data, 333
333
done just like before, 334
334
we will have a parent element. 335
335
So just like in the recipe view, 336
336
so a parent element, 337
337
and then we will set that to something, 338
338
query selector, and now let's see what we get 339
339
here in the DOM. 340
340
So in the HTML, 341
341
so the search, 342
342
now, it's this one. 343
343
So we have this entire form, 344
344
which is called search 345
345
and which has this input field here 346
346
and also the button. 347
347
And so I think that this is the element 348
348
that makes sense to select. 349
349
So the one with the class of search, so .search. 350
350
So this is our parent element here in this case. 351
351
Now, again, this class is not going to render anything. 352
352
All we want is to get the query 353
353
and eventually to also listen for the click event 354
354
on the button, 355
355
but let's start with the query here. 356
356
And so, let's create a method 357
357
that we will then call from the controller called getQuery. 358
358
And all this will do 359
359
is to return this.#parentElement. 360
360
And so again, 361
361
that's going to be the search. 362
362
And then from there, 363
363
we want to select this input field. 364
364
So this one with this class. 365
365
And so let's a select this child element 366
366
using a querySelector, 367
367
and then simply get the value of that. 368
368
Okay, and that's actually it. 369
369
And again, of course 370
370
we could have written this exact same code 371
371
in a controller, 372
372
but that wouldn't make any sense, 373
373
because this is not concerned about the DOM at all. 374
374
So we don't want to have any DOM elements in here. 375
375
We don't even want to know what the DOM looks like. 376
376
So actually here we have this, 377
377
but we don't want this here. 378
378
Let's get rid of all this clutter here actually. 379
379
And now, I want to 380
380
get the searchView here. 381
381
And then here, 382
382
we can get that query 383
383
by saying searchView.getQuery. 384
384
And of course, 385
385
there might be no query. 386
386
And so, let's add another guard clause here. 387
387
So if there is no query, 388
388
then return immediately. 389
389
And so here, 390
390
we will now no longer look for pizza, 391
391
but for query. 392
392
Now, if we run this, 393
393
then of course there will be no result 394
394
because, well, right now, 395
395
there is nothing here to be found. 396
396
So that controller function 397
397
runs right in the beginning 398
398
when the application actually loads. 399
399
And so by then of course, 400
400
there is a no value here. 401
401
And so, in order to actually make this work, 402
402
we now need to listen for the event 403
403
of basically clicking this button or submitting this form. 404
404
And then, only on that event, 405
405
we want to actually call, 406
406
this controller function here. 407
407
So not in the beginning when the script loads. 408
408
So, in order to do that, 409
409
we will once again, 410
410
use the publisher subscriber pattern. 411
411
So basically exactly what we did here, right? 412
412
So we will list in for the event 413
413
in the view right here, 414
414
and then pass the controller function. 415
415
So the handler function 416
416
into the method that we will build here. 417
417
So this addHandler, a search method here 418
418
is basically going 419
419
to be the publisher anti-control search results function 420
420
is going to be the subscriber. 421
421
Okay, so hopefully you really understood that pattern 422
422
that we used before. 423
423
And so this now, 424
424
it should be obvious. 425
425
So, let's take our parent element. 426
426
So this.#parentElement of course, 427
427
and then this is where 428
428
we will actually add the event listener. 429
429
So not to the button, 430
430
but really to the entire form 431
431
because there, we can listen for the submit event. 432
432
And so then, this will work 433
433
no matter if the user clicks the submit button 434
434
or if the user hits Enter while typing the query. 435
435
Now here, we cannot simply call the handler immediately, 436
436
because remember, as we did many times before 437
437
when we submit a form, 438
438
we need to first prevent the default action, 439
439
because otherwise the page is going to reload. 440
440
So, preventDefault. 441
441
And only after that, 442
442
we can call the handler function. 443
443
And, as you already know, 444
444
this handler function 445
445
should be the control searchResults function. 446
446
And so, all we have to do now 447
447
is to call this method 448
448
and pass in that function. 449
449
So, let's do that here right at the beginning. 450
450
So searchView.addHandlerSearch. 451
451
So you'll see that I'm always going to use 452
452
this name here of addHandler, 453
453
and then we pass in the controller function 454
454
and that's it. 455
455
So now it should actually already work. 456
456
So let's see. 457
457
And actually, let's clear up all this clutter 458
458
here a little bit. 459
459
Okay, so this console.log, 460
460
and there's also some here in the recipe view. 461
461
So that's this one. 462
462
Okay, so that's again, try pizza 463
463
and yeah, beautiful, it worked. 464
464
So we get 465
465
this data first here coming from the model, 466
466
and then also in a controller, 467
467
we also logged it. 468
468
And so here we have the exact same 59 data 469
469
that we had before. 470
470
Let's try something else now, 471
471
avocado, let's say, 472
472
so I hit Enter, 473
473
and, so this time we got 39 search results 474
474
and you see, 475
475
all of them basically have avocado 476
476
right there in the title, 477
477
it's really beautiful 478
478
that it already works this great. 479
479
Now just one small detail, 480
480
is that, after we search, 481
481
we should probably clear out this field. 482
482
So let's add another small method for that in the view. 483
483
So, and this one here, 484
484
so clearInput, 485
485
and once again, 486
486
having it nicely encapsulated inside of the searchView. 487
487
So again, this could simply be also in a controller, 488
488
because, it's really just one line of code, 489
489
but we want to keep everything separated. 490
490
This will make it so much easier to add features 491
491
in the future. 492
492
And it's really so much better. 493
493
So, all we do is to set this to empty and that's it. 494
494
All right, let's try it one more time. 495
495
Let's say pasta this time. 496
496
Ah, of course we never called DOM method here anywhere. 497
497
So we need to do that in the controller. 498
498
And let's actually add some more comments here. 499
499
So Get search query. 500
500
And the code itself is actually pretty self explanatory. 501
501
And so many developers will tell you 502
502
you should not comment it, 503
503
but I think 504
504
that this is actually very helpful when you read the code 505
505
in like one or two years from now. 506
506
So, I would advise you to always write comments like this 507
507
even if your code seems pretty obvious, 508
508
especially when it's like 509
509
a bigger list of instructions like this, 510
510
so Render results. 511
511
So right now our rendering is only a console.log. 512
512
But here, when we get a search query, 513
513
we can actually then 514
514
immediately also clear it afterwards. 515
515
Or in fact, 516
516
in order to keep this controller even simpler, 517
517
we can do that 518
518
right in the getQuery method. 519
519
So, let's go back here, 520
520
and now we can make this private, 521
521
so we're not going to use it outside. 522
522
And then let's actually call it here. 523
523
So this.#clearInput. 524
524
And then of course, 525
525
we first need to store this somewhere here. 526
526
So we get the query, 527
527
then we clear the field 528
528
and then we return the query. 529
529
All right, so that should now work. 530
530
So pasta and clears the field, 531
531
and gives us or 45 results down here about pasta. 532
532
Great, so that works just fine. 533
533
And so, all there's left to do 534
534
is to now implement actually the view. 535
535
So the second view basically. 536
536
So the one 537
537
that is responsible 538
538
for then rendering all the results 539
539
here in this sidebar. 540
540
And so that's going to be a little bit of work. 541
541
So let's leave that for the next video.
Can't find what you're looking for?
Get subtitles in any language from opensubtitles.com, and translate them here.