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 render the search results 2
2
by creating a new view. 3
3
So we already have the searchView 4
4
but now we also want to create like a resultsView. 5
5
So a resultsView, which will contain a render 6
6
so that we can then call that render here 7
7
with these search results. 8
8
So similar to what we have here, 9
9
so to the recipeView, 10
10
which also has a render method, 11
11
which takes in some data 12
12
and so now, again we want the same 13
13
for a results view. 14
14
So we also want it to have a render method, 15
15
which takes in data and which is then rendered 16
16
to our user interface. 17
17
So let's create the resultsView.js. 18
18
So ResultsView 19
19
and so now this view 20
20
will actually be pretty similar to the recipeView. 21
21
So we don't need that one anymore. 22
22
But now, again this one is going 23
23
to be very similar. 24
24
So we will have a parentElement, 25
25
data, these messages. 26
26
We will basically also need the same render method, 27
27
the same renderSpinner. 28
28
So we will need to display the spinner 29
29
while the data is being fetched, 30
30
so in this case, the search results. 31
31
We might need to also render an error 32
32
and so now it's time to basically refactor this code 33
33
and create a parent class. 34
34
So I had mentioned that before 35
35
but now it's actually time to do it 36
36
because by creating a parent class, 37
37
we will then be able to reuse all of these methods 38
38
on all the views. 39
39
So basically, on all the objects 40
40
that are created through all the view classes. 41
41
So let's create yet another file here 42
42
and I'm calling it View.js. 43
43
And so let's create a new class here called View 44
44
and so we will then export immediately this class. 45
45
And actually, let's make it a default export. 46
46
So at this time, we are actually exporting the class itself 47
47
because of course, we are not going to create any instance 48
48
of this view. 49
49
We will only use it as a parent class 50
50
of these other child views, all right? 51
51
Here in the recipeView, 52
52
let's now import that parent class. 53
53
So import View as, 54
54
or actually, not as but from View 55
55
and actually, we don't even need the js 56
56
but I like to keep it there for consistency. 57
57
And so now, all we need to do 58
58
is to basically copy everything 59
59
that should be common to all the views 60
60
to that parent class. 61
61
However, there is one thing 62
62
that we still need to change here. 63
63
And that is that right now, 64
64
with Parcel and Babel, 65
65
inheritance between these truly private fields 66
66
and methods doesn't really work yet. 67
67
So here it was nice to actually use the native way 68
68
of JavaScript of protected methods and properties 69
69
but now we cannot really use it anymore. 70
70
So maybe that might be possible at some point in the future 71
71
but for now, we will have to go back to protected fields 72
72
and protected methods. 73
73
So let's basically select all of these hash symbols 74
74
and actually, I will select them like this. 75
75
So with the dot because otherwise, 76
76
that will also select these ones. 77
77
So hitting Command + D here a couple of times. 78
78
And then replace it with ._ 79
79
and now we just have to change these method names. 80
80
So changing from truly private 81
81
to only protected using the underscore convention. 82
82
And then the same right here. 83
83
Okay? 84
84
So now our code should still be working. 85
85
It says here connection lost 86
86
but indeed, our code keeps working here. 87
87
Okay? 88
88
Let's just check out the search view 89
89
and so let's use the same convention here as well. 90
90
Okay. 91
91
Just here as well. 92
92
And now here we also converted everything successfully. 93
93
But anyway, let's now basically extract the View class 94
94
from this RecipeView. 95
95
So what we need in all the views 96
96
is this render method, 97
97
then this one as well, this one, 98
98
and basically everything except for generating the markup 99
99
because that, of course, 100
100
is gonna be unique to every single view, right? 101
101
And also, adding the handler. 102
102
But everything else we want to be the same on all the views. 103
103
For example, rendering a spinner 104
104
will be really easy. 105
105
So all the views will simply have this method 106
106
and we will then be able to call it on that method 107
107
and thanks to the parentElement property here, 108
108
it will then know exactly 109
109
to which element it should attach the spinner 110
110
because again, the parentElement, 111
111
of course, will be unique to each single view. 112
112
So the parentElement will be unique, 113
113
the message, so these two messages 114
114
but this data can actually also be on the parent. 115
115
So let's copy all of this here or actually cut. 116
116
So everything except for adding the handlers 117
117
and generating the markup. 118
118
So let's put that here. 119
119
And here we, of course, then need to extend this one. 120
120
So extends View. 121
121
Right? 122
122
And so let's see if we have everything still in place. 123
123
Yeah, so I'm confident that this will work now. 124
124
So let's see. 125
125
Oh, and icons is not defined. 126
126
So let's go back here to our view 127
127
where we will have to copy this one. 128
128
Because here, of course, we are also using the icons. 129
129
Okay. 130
130
But here it is. 131
131
All right? 132
132
So we successfully extracted the View class 133
133
from the recipeView. 134
134
And now all that's left here 135
135
is really what is unique to this view. 136
136
And so this makes this even easier to work with 137
137
in my opinion because now let's see what we can do here. 138
138
So even without any code yet in the results view, 139
139
we will already be able to add a spinner 140
140
to the search results. 141
141
All right? 142
142
Well, we cannot really leave it empty. 143
143
We still need actually the parent element here. 144
144
All right? 145
145
And we also need to import the View. 146
146
So import View 147
147
and then this is just another child class of View. 148
148
Then here, we need to say that parentElement 149
149
should be well, something. 150
150
And let's check out what it actually should be. 151
151
So here in index, 152
152
let's see. 153
153
So it's this here. 154
154
So this results element, so this unordered list. 155
155
All right, and then basically, 156
156
each of the search-results will be one 157
157
of these preview elements. 158
158
But more about that later. 159
159
So for now, let's just set the parentElement 160
160
to this results element here in resultView. 161
161
So document.querySelector results. 162
162
And then we have to export 163
163
default just like before 164
164
and then a new instance of this view. 165
165
And so this way they can only be one ResultsView 166
166
and then here in controller, 167
167
we can immediately import that 168
168
without having to create the instance here manually. 169
169
So that's resultsView like this. 170
170
And so now here we should already be able 171
171
to load the spinner 172
172
or actually to display the spinner. 173
173
So let's do that at the very beginning. 174
174
So resultsView.renderSpinner. 175
175
And let's see what happens. 176
176
So pizza and here is a small error 177
177
and so let's once again do some debugging here 178
178
and I hope that you can appreciate 179
179
that I'm actually not cutting out 180
180
all these like small debugging sessions 181
181
so that you can actually see 182
182
what I'm doing here. 183
183
But anyway, apparently the problem appears here in View.js 184
184
at line 14. 185
185
So right here. 186
186
So it seems like it cannot find the parentElement. 187
187
So let's simply log that to the console. 188
188
So this here is the result of doing that for the view. 189
189
And now let's see. 190
190
So now that is undefined. 191
191
So to me it seems 192
192
like here we selected the wrong element here maybe. 193
193
So results. 194
194
So that should actually 195
195
be correct. 196
196
And so should the name. 197
197
So I think parentElement is correctly spelled. 198
198
Oh, but it's actually called parentElement all written out. 199
199
Okay. 200
200
So you see, it's actually called parentElement like this. 201
201
For some reason, I thought it was called 202
202
just like the abbreviation. 203
203
Ah. 204
204
And you see, here is the nice loading spinner, 205
205
spinning away here in our search results. 206
206
Right? 207
207
And again, all without writing any new code 208
208
for displaying that spinner here in the ResultsView 209
209
because it is now simply inheriting that method 210
210
so that renderSpinner method from its parent class. 211
211
So let's get rid of this 212
212
and just for the sake of curiosity, 213
213
let's actually take a look 214
214
at this resultsView, 215
215
so just so we can see the whole prototype chain here 216
216
in the console. 217
217
So for that, we need that controller to run 218
218
and so here it is. 219
219
So you see that we have still some undefined data. 220
220
Then we have the parentElement. 221
221
So the one we selected. 222
222
And then we get, 223
223
well, right now, we don't have any methods 224
224
in the class already 225
225
but we do have methods in a parent class. 226
226
And so these are the ones. 227
227
Right, so the render one that we're gonna use next, 228
228
then renderError and Spinner and Message. 229
229
So it's always good to remember these things 230
230
so that we still know how everything still actually works 231
231
behind the scenes. 232
232
But now it's actually time 233
233
to create basically this generateMarkup method also 234
234
in the resultsView, okay? 235
235
So all the child views should have this method here 236
236
because in the view, the render method then actually relies 237
237
on that, okay? 238
238
So this.generateMarkup needs to exist in order 239
239
for the render method then being able 240
240
to create the markup and render it 241
241
or to insert that HTML into the DOM. 242
242
And so let's take that, just that name 243
243
and create that right here. 244
244
So as always, a template literal 245
245
and then now it's time to actually get this code here. 246
246
Okay? 247
247
And this here is called a preview 248
248
because we will actually use the same thing later here 249
249
to render the bookmarks. 250
250
So that's gonna be a nice trick 251
251
but we will save that for the future. 252
252
Okay? 253
253
Some more indentation here on all of this 254
254
to make it look great. 255
255
And so now we need to take our data 256
256
and actually put that right here. 257
257
Now, where actually is that search results data? 258
258
So with all of the structure, 259
259
it might be a little bit hard to follow 260
260
where that data is. 261
261
So let me actually start 262
262
by calling the render method here. 263
263
So here, besides of simply logging the search results, 264
264
we now also want resultsView.render 265
265
because of course, just like renderSpinner, 266
266
the resultsView also inherits render 267
267
and then into that, we pass whatever we want to be rendered. 268
268
And so that is all of this. 269
269
Okay? 270
270
And then in the render, 271
271
here we see that it takes this data 272
272
and puts it into this._data. 273
273
And so here, if we then take a look at the resultsView, 274
274
let's simply log that then to the console. 275
275
So this._data 276
276
and so this_data here 277
277
should basically be exactly this search.results. 278
278
So let's see. 279
279
And it is. 280
280
So you see that twice we now get this array 281
281
and you also see that it already printed something here 282
282
to the results. 283
283
So that is that pre-formatted HTML 284
284
that we already created here. 285
285
So it's already basically this HTML, right? 286
286
Because this method here is already returning that 287
287
and so then the view takes that data 288
288
and renders it on the user interface. 289
289
Okay? 290
290
And so let's now actually create this method. 291
291
Now, this is actually an array now 292
292
and so here we need to loop over that array. 293
293
So in fact, we don't want to return simply this 294
294
but instead, we want to return a whole string, 295
295
which contains like one of these elements 296
296
for each of the search results in the array. 297
297
So we will return basically this._data 298
298
and then we map over it. 299
299
Remember, so we did that before with the ingredients 300
300
and then at the end, 301
301
we join everything together 302
302
and so then that will be the string 303
303
that we return here. 304
304
Now, we could now go ahead 305
305
and put this code here into the map function 306
306
but instead, I will create just another method down here 307
307
to make this a little bit cleaner. 308
308
Let me copy this name. 309
309
Preview. 310
310
And I'm simply calling it this 311
311
because that's also the class name. 312
312
So let's put this one here. 313
313
And so then here we can call the method. 314
314
Oh, what was that? 315
315
So we can call it right here then. 316
316
And so now this should like print 50 or 60 of the same. 317
317
So just to see if it works. 318
318
And indeed, here it is. 319
319
So 59 results 320
320
and so it prints this 59 times right here now. 321
321
Right? 322
322
Now, all we need to do is to fill it 323
323
with the real data. 324
324
So with the id, image, publisher and title. 325
325
Okay? 326
326
So to start, we need the ID here 327
327
in order to build the link 328
328
and so then we click the link, 329
329
that will change the hash in the URL, 330
330
which in turn, will trigger a loading a recipe. 331
331
So that's what we did by the beginning of this section. 332
332
Now, here we need to pass in the result then. 333
333
And so here that is result.id. 334
334
Then here it's the image, 335
335
so .image. 336
336
Here it's the title. 337
337
And then here the publisher. 338
338
Okay. 339
339
Here we also need to replace the icons. 340
340
So we will need again the icons right here. 341
341
We will probably need them in all of them. 342
342
But that should be it. 343
343
And it works. 344
344
Beautiful. 345
345
So we get all of these search results here. 346
346
We just have to fix some small details 347
347
but you see that it's already working 348
348
and now let's click on one of them and it works. 349
349
Minimizing this a little bit 350
350
and so you see, now we get exactly the recipe 351
351
that we are clicking on here. 352
352
So watch what happens with the ID 353
353
when I click here on Pizza Casserole 354
354
or Casserole or whatever. 355
355
So it changed the ID up there 356
356
and changed the recipe here also in the view. 357
357
Great. 358
358
And now let's just fix this icon here 359
359
and actually, also here. 360
360
And then also all of these should not be highlighted. 361
361
We will take care of that a little bit later. 362
362
So we need to take away this class here of active. 363
363
And here we also need to take away this. 364
364
Okay? 365
365
And we will come back to this later again 366
366
when we actually start 367
367
to implement uploading our own recipes. 368
368
And here in the alt text, 369
369
we should just put that title as well. 370
370
Okay? 371
371
Now you see that actually the states 372
372
that we had here before in the page is gone 373
373
and so let's activate that hot module reloading 374
374
that I mentioned in the last section. 375
375
So remember that with parcel, we can do this. 376
376
So if module.hot, 377
377
then module.hot.accept. 378
378
All right? 379
379
And again, this is not real JavaScript, 380
380
at least this module.hot. 381
381
This is simply coming from parcel. 382
382
All right? 383
383
And now finally, let's also delete these fakes links 384
384
that we had before. 385
385
Let's reload. 386
386
And that looks a lot better. 387
387
Okay? 388
388
So let me now change something in the code. 389
389
For example, getting rid of this console.log here. 390
390
And so previously, that would then reload the entire page 391
391
but now, as we go back, 392
392
you see that the state here actually remains. 393
393
All right? 394
394
All it says is the console was cleared but that's it. 395
395
So the rest remains the same 396
396
and we can now also delete this user icon 397
397
from the recipeView. 398
398
So let's see where that is. 399
399
It is simply this div here. 400
400
And so once again, it will now not re-render everything. 401
401
Well, maybe we should re-render. 402
402
And yeah, now it's gone. 403
403
Well, it also messed up our button here for some reason. 404
404
So maybe that was not intended. 405
405
Maybe I should just remove this here. 406
406
So basically keeping an empty div. 407
407
Okay, so for the time being, 408
408
let's just leave it like this. 409
409
Okay. 410
410
Now, let's just see what happens when we search something 411
411
that for sure does not exist. 412
412
So you see that here we still get a success. 413
413
You see. 414
414
So we still get that data but the only difference 415
415
is that the recipes array is now empty 416
416
and here we also see that the array is empty coming 417
417
from the state. 418
418
So in this case, we actually want 419
419
to render like an error message here saying 420
420
that there was no result for that query. 421
421
So let's start by creating also 422
422
the errorMessage property here. 423
423
So let's copy these two actually to our resultsView. 424
424
And here let's say no recipes found for your query! 425
425
Please try again. 426
426
But now, how exactly 427
427
will we actually display this errorMessage 428
428
on the parentElement? 429
429
So we could do that right here in generateMarkup 430
430
and basically check if the array is empty 431
431
and if it is so, 432
432
then render the errorMessage. 433
433
So that would work. 434
434
However, we can also do that directly 435
435
in the render method. 436
436
So automatically, right as we receive the data. 437
437
And I believe that is what makes more sense. 438
438
So right in the beginning 439
439
when the render method is first called 440
440
and receives the data for the first time, 441
441
we can immediately check if that data actually exists. 442
442
So right now, we only simply assumed 443
443
that there always is data 444
444
and that we can then generate some markup with that data. 445
445
But as always, it's a good idea 446
446
to check if the data actually exists. 447
447
And that is true no matter if we receive a simple object 448
448
or if we receive an entire array of objects, 449
449
which is the case of the results. 450
450
So let's add a guard clause here. 451
451
So if there is no data, 452
452
then we want to return immediately 453
453
and we also want to render the error. 454
454
So we can do that all in one line 455
455
and this will then still work. 456
456
So this.renderError 457
457
and we actually don't even have to pass in any message 458
458
because we already automatically get the message 459
459
from this._errorMessage. 460
460
And so in this case, that is going to be this one 461
461
that we just specified before. 462
462
Right? 463
463
Now, however, in this case, 464
464
this check is actually not enough 465
465
because in fact, we do get data. 466
466
It's just an empty array 467
467
and so in this case, 468
468
we also want to treat the empty array 469
469
as though we had no data. 470
470
So this here basically only works for undefined 471
471
or for null. 472
472
But now we also want to check if the received data 473
473
is an array and if it is empty. 474
474
So we can do that by saying 475
475
so if there is no data or 476
476
and then basically, if the data is an array 477
477
and if it's empty. 478
478
And we can do that 479
479
by using a helper function 480
480
that is on the Array constructor, 481
481
which is isArray. 482
482
So we can check that. 483
483
And so if it's an array 484
484
and the length of the array is zero basically, 485
485
data.length zero, 486
486
well, then in this case, 487
487
exit this function immediately 488
488
and also render the error, okay? 489
489
So again, if there is no data 490
490
or if there is data, 491
491
but that data is an array, 492
492
and it is empty. 493
493
So in both these cases, we want the error to be shown. 494
494
So let's check that. 495
495
So with pizza, it all works the same 496
496
because there are results. 497
497
But now with this, ha, there are no results 498
498
and then we get our error message. 499
499
So great. 500
500
That works. 501
501
And so I think we are done actually with this part. 502
502
So just one more look at our flowchart. 503
503
So we implemented the event 504
504
that the user searches. 505
505
We asynchronously loaded the search results 506
506
and then we also rendered them to our user interface. 507
507
And so with that, we are done. 508
508
Right? 509
509
Just a final look here at the controller 510
510
that everything looks good. 511
511
So in the next lecture, we will actually take care 512
512
of the problem that we have these 59 results, 513
513
all one after another here. 514
514
So that's not good 515
515
and so we will implement pagination 516
516
in the next two lectures.
Can't find what you're looking for?
Get subtitles in any language from opensubtitles.com, and translate them here.