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
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
Interlingua
Irish
Italian
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 continue working 2
2
on the recipe uploading feature. 3
3
And this time we will actually work on the model 4
4
which will be responsible for sending the recipe data 5
5
to the Forkify API. 6
6
So let's go to our model. 7
7
And then down here after the bookmarks 8
8
let's create a new function and export it, 9
9
called upload recipe, okay. 10
10
And this one will eventually make a request to the API. 11
11
And so therefore it's going to be an async function 12
12
and it will receive the data for a new recipe. 13
13
Now, the first task of this function 14
14
here will be to take the raw input data 15
15
and transform it into the same format as the data 16
16
that we also get out of the API. 17
17
So let's take a look at that. 18
18
Now, it reloaded the page, of course. 19
19
So basically the format of the data that we receive 20
20
from the API looks a little bit like this. 21
21
Now, it has some different property names here 22
22
but what really matters 23
23
here is that the ingredients are nicely stored 24
24
in this array. 25
25
So there is an ingredients array 26
26
which contains a bunch of objects 27
27
where each contains quantity, unit, and description. 28
28
However, the data that we get into the function 29
29
right now simply has these three 30
30
or actually these six ingredient properties. 31
31
So ingredient one, two, three, four, five, and six. 32
32
And also it has each of these ingredients 33
33
here separated with these commas, all in one string. 34
34
And so now we need to basically put all of this data 35
35
here into one array and also separate each of these strings 36
36
into the quantity, unit, and description. 37
37
Now, of course, here in the form I could have also created 38
38
three different fields for each ingredient. 39
39
So one input field for the quantity, 40
40
the unit, and to description. 41
41
But that was a little bit too much work 42
42
for this learning project 43
43
and so I decided that I will simply give this hint 44
44
that the format needs to be quantity, unit, description. 45
45
And so that's what we have here. 46
46
So half a kilo, so kilo is to unit, 47
47
and then rice is to description. 48
48
Then here we need one avocado but there is no unit 49
49
and so we leave that one empty. 50
50
And then here we have just salt, 51
51
so without any quantity and any unit. 52
52
And so let's now take this data. 53
53
So these six ingredients and then take the data 54
54
out of there and put them into an object. 55
55
So this is how we will do it. 56
56
So we want to create an array of ingredients. 57
57
And so for that, 58
58
a good idea is to use the map method, right? 59
59
So map is always good to create new arrays 60
60
based on some existing data. 61
61
So in order to use map, we need an array. 62
62
And so let's convert the object 63
63
that we get here back to an array. 64
64
So object.entries, 65
65
and as I mentioned, this is basically the opposite 66
66
of object.fromEntries, 67
67
but it still makes sense to convert it to an object 68
68
here because we will also need the rest of the data 69
69
here as an object. 70
70
So new recipe, 71
71
and then we want to filter this array 72
72
because remember we only want the properties 73
73
that are called ingredient one, two, 74
74
three, all the way until six. 75
75
So in this array that we have right now, 76
76
each element is an entry, right? 77
77
And once again, let me actually show that to you. 78
78
So without seeing the data 79
79
I think that it becomes very difficult to follow this. 80
80
So I will console.log this data 81
81
here and now of course, we need to call this function, 82
82
upload recipe in the controller. 83
83
So let's not do this, 84
84
but let's actually call model.uploadRecipe 85
85
with the new recipe. 86
86
Now, okay, let's click here. 87
87
And so again, that is our array that we will work with now. 88
88
And so remember right now we are filtering this array. 89
89
And so what we want is basically only the entries 90
90
where the first element of the array starts with ingredient 91
91
like this, right? 92
92
And then also the second part should not be empty. 93
93
So in this case, we are of course not interested 94
94
in four, five, six. 95
95
Okay, so here add this condition 96
96
that we need for the filter. 97
97
We want to say, just as I explained just a second ago. 98
98
So the first element of the current entry should start 99
99
with ingredient and the second one should exist, 100
100
so it should not be an empty string. 101
101
So entry one, 102
102
so the second element should not be an empty string, okay. 103
103
And so this should now filter out only the data 104
104
that we are interested in. 105
105
So let's log at to the console and not this anymore. 106
106
And yeah, so that is what we were looking for. 107
107
And now all we need to do is to basically take the data 108
108
out of the string here and then put that into an object. 109
109
And so for that, we now finally use the map method. 110
110
And so here we already have some ingredients. 111
111
And so now we will take each ingredient 112
112
and split it by the comma, 113
113
so that's the separator that I specified. 114
114
And before we do that, 115
115
let's actually make sure that we remove 116
116
any white space here. 117
117
So we can simply do .replace and I believe that right now 118
118
the replace all method is actually already available. 119
119
So remember in the video where we talked about strings 120
120
I mentioned that replace all 121
121
will soon be available in browsers. 122
122
And I believe that right now it already is. 123
123
And so let's use that here. 124
124
So let's replace all the basically spaces 125
125
with an empty string. 126
126
And so this should then return an array of three elements, 127
127
and so let's not destruct that array. 128
128
So the first element is the quantity, 129
129
the second one is the unit, 130
130
and the third one is the description. 131
131
And now, let's return an object with this. 132
132
So with quantity, unit, and description. 133
133
We will still have to do some things 134
134
here but for now let's take a look at what we get. 135
135
And ingredient.replaceAll is not a function. 136
136
So that's because each ingredient 137
137
here is actually still an array, 138
138
so just like we had it here before in the entry. 139
139
So the first element of entry here remember was the key 140
140
and the second one was the value itself, 141
141
so here we need to use this on ingredient one. 142
142
So the second element of the entry. 143
143
So let's see now, and here we go. 144
144
So we have quantity, unit, and description 145
145
nicely inside of one array. 146
146
And so that's now kind of looks like this, great. 147
147
Now, there is just one thing that we need to notice here, 148
148
which is that when the quantity does not exist 149
149
then we want it to be no. 150
150
And otherwise it's actually a number. 151
151
So currently we still have strings 152
152
here but we want us to be a number now, 153
153
and so let's go ahead and convert that. 154
154
So let's come here. 155
155
So the quantity should be, so if there is a quantity, 156
156
so let's use the ternary operator. 157
157
So if there is a quantity, then convert that to a number. 158
158
All right, and if not, which is the situation 159
159
in which it is an empty string, then we want to return null. 160
160
So that should fix that, and indeed it does. 161
161
But now, let's see what happens 162
162
when we simply put something else here, 163
163
let's say just a number. 164
164
So let's see. 165
165
Well, then here we get a bunch of undefined 166
166
and so that we don't want, 167
167
so our format 168
168
here should always contain quantity, unit, description. 169
169
So in this case, what we would need to do is this. 170
170
So this is what we would expect. 171
171
That's so then we get here the empty strings as expected. 172
172
So basically what this means is that when we split 173
173
this string here, we wanted to have three parts, all right? 174
174
And so let's actually convert that to a code. 175
175
So here we want to now put 176
176
this here into a separate variable. 177
177
So let's call it ingredients array, 178
178
and then that is the array that we destructure. 179
179
But before we do that we can test 180
180
if this array actually has the length of three. 181
181
So if ingArray.length is different from three 182
182
then we want to create a new error. 183
183
So throw new error, 184
184
and then let's say wrong ingredient format, 185
185
please use the correct format. 186
186
And so then at this point here, 187
187
the function will immediately exit. 188
188
Now, then what do we want to actually happen 189
189
with this error? 190
190
Well, we will want to render an error message to this view. 191
191
So to the add recipe view, right? 192
192
And that is very simple. 193
193
So here we can use all try catch 194
194
just like we have been doing before. 195
195
So try and so if some error occurs in this function here, 196
196
then it goes to the catch block as always. 197
197
So here we get the error and then let's log that error 198
198
to the console again, with some emojis here to make it easy, 199
199
to understand that it is our own error. 200
200
And then we can simply say, addRecipeView.renderError. 201
201
And now we can actually pass in the error message 202
202
ourselves because here in the view 203
203
we accounted for that ability. 204
204
So by default the error message 205
205
is coming from this .errorMessage 206
206
but we can also specify our own. 207
207
And so that is what we will do now 208
208
because this way we are able to create the error message 209
209
basically here where the error actually occurred. 210
210
Okay, so error.message, and so now let's see what happens. 211
211
So for now, let's use the correct format 212
212
just to see if everything keeps working, 213
213
and it does. 214
214
But now, let's do it wrong 215
215
and we get wrong ingredient format 216
216
but it is still coming from the model. 217
217
So here we got uncaught in promise 218
218
and that actually makes sense, 219
219
because remember that here we are in 220
220
an async function already. 221
221
So this returns a promise 222
222
and so basically this then rejects the promise. 223
223
And so therefore we should handle that already right here. 224
224
So try like this and then catch the error 225
225
and simply throw that error again. 226
226
Okay, let's try that again. 227
227
Okay, and that still doesn't look 228
228
the way we want it to look. 229
229
So we wanted here to be an error message, 230
230
and so that should come from the controller. 231
231
And so that means that there is something wrong 232
232
in the controller. 233
233
And I guess I know why that happens 234
234
because remember that this is an async function right here. 235
235
So it returns a promise but then right 236
236
here we are actually not awaiting that promise. 237
237
So we need to use await 238
238
here and then also make this an async function. 239
239
And so only with this, 240
240
we are now actually handling this function 241
241
here as a function that returns a promise 242
242
so that the rejected promise can then actually get caught 243
243
here in this catch block. 244
244
So let's try once again here with some number. 245
245
Ah, and now it works. 246
246
So now, we get the actual error with these emoji mark 247
247
and also the error that we want here, okay, great. 248
248
So we have that part working of the ingredients. 249
249
Now, it's time to actually create the object 250
250
that is ready to be uploaded. 251
251
So let's create a recipe object here. 252
252
And so basically this object will be the opposite 253
253
of this one here. 254
254
So here we take in the source URL formatted as this, 255
255
and we want to send the data where the URL looks like this. 256
256
So where the property has this format, 257
257
it's the same here for the URL and to cooking time actually, 258
258
because that is exactly the format 259
259
that the API is ready to receive. 260
260
So it has to be exactly the way that we received it. 261
261
Okay, so let's write that object very quick. 262
262
So that is at newRecipe.title 263
263
then the source URL is that newRecipe.sourceUrl, like this. 264
264
Then the image URL is newRecipe.image, right? 265
265
Then the publisher is actually the same, 266
266
newRecipe.publisher. 267
267
The cooking time, newRecipe.cookingTime. 268
268
So fortunately, VS Code is giving us 269
269
already the correct suggestions, .servings. 270
270
And so that's all I think. 271
271
And so all we have to do now is to add the ingredients 272
272
array, and that's it. 273
273
And then we also have two numbers 274
274
here which we should convert to numbers 275
275
using this plus trick, all right. 276
276
And so now, let's actually log this one to the console 277
277
and not just the ingredients. 278
278
Oh, and of course, all this should be happening 279
279
inside of this try block, right. 280
280
So let's see, okay. 281
281
That looks great. 282
282
And it looks ready to be actually sent to our API, okay. 283
283
So let's do that. 284
284
So remember we already have a method for getting JSON. 285
285
So that's this one and now let's create a method 286
286
for sending JSON here in our helper. 287
287
And actually it's going to be very similar. 288
288
So for now, let's just copy it, 289
289
but then a bit later we will simply refactor this. 290
290
So here let's call it send JSON, all right. 291
291
And so now, let's learn how we can actually send data 292
292
using the fetch function. 293
293
So up until this point, 294
294
all we ever did was to simply pass in a URL 295
295
into the fetch function 296
296
and that would then automatically create a get request. 297
297
However, to send data, 298
298
remember how we discussed 299
299
before that that needs to be a post request. 300
300
And so here besides passing in the URL 301
301
we also need to pass in an object of some options, okay. 302
302
And the first option is going to be the method. 303
303
And so that method is now post. 304
304
Then we also need to pass in an object of headers, 305
305
and headers are basically some snippets of text, 306
306
which are like information about the request itself. 307
307
And many of them are like standard headers. 308
308
And one that we need to define is the Content Type. 309
309
So Content Type with an uppercase C and a T, 310
310
so make sure you get that right. 311
311
And then here application/JSON, 312
312
and so with this we tell the API. 313
313
So we specify in the request 314
314
that the data that we're gonna send 315
315
is going to be in the JSON format. 316
316
And so only then our API can correctly accept that data 317
317
and create a new recipe in the database. 318
318
And now, finally the payload of the request. 319
319
So basically the data that we want to send, 320
320
which is called the body. 321
321
So the body should be in JSON. 322
322
Remember as I just explained, 323
323
and so we can once again use JSON.stringify 324
324
and then convert the data that we want to send. 325
325
Now, and here we actually need to accept that data 326
326
here into the function, of course, as a second parameter. 327
327
So let's call that upload data. 328
328
And so that is the data that we then convert to JSON 329
329
so that it can be sent, and that's actually it. 330
330
So that's all we need to do. 331
331
So we still pass in a URL 332
332
which we will need to specify next. 333
333
And then we take that URL and the upload data, 334
334
and then simply create this fetch request like this, okay. 335
335
And then everything else is actually going to be the same. 336
336
So we will still have this request a racing about a timeout 337
337
function so that it can't run forever. 338
338
And we will also await any data 339
339
coming back because this API that we're gonna use 340
340
so the Forkify API will actually return the data 341
341
back that we just sent. 342
342
And you will see in a moment 343
343
why that is very important, okay. 344
344
So again, everything here is going to be the same. 345
345
And so a bit later we will then refactor these two 346
346
here into the same function, but let's do that later. 347
347
So here we now need to import the send JSON function. 348
348
And so now, let's use our newly created function 349
349
to create the Ajax request. 350
350
So send JSON and now the URL of the API, 351
351
and actually we have that here stored somewhere, 352
352
so that's this URL that we have in the config, right? 353
353
So slash recipes, 354
354
but that's actually not all as we will see in a second. 355
355
All right, let's get rid of this. 356
356
And now, let's go to the Forkify documentation page. 357
357
So at this URL, because now we actually need the API key. 358
358
So please click here now on this button, 359
359
which will then generate your unique developer key. 360
360
Now, if you got some kind of error 361
361
here while generating your key 362
362
then maybe it is because you requested more than one 363
363
API keys per hour. 364
364
So remember that you should only ever request one API key. 365
365
That's why I have this limit here in place 366
366
so that no one can abuse my API basically. 367
367
Now, if you did actually only try it once 368
368
and you still got an error, 369
369
then you can always contact me on Twitter, okay. 370
370
So here's the link to my Twitter page 371
371
and then just tweet the error at me 372
372
and I will try to help you. 373
373
Now, my key disappeared here, 374
374
so let's click again and now I will copy 375
375
it so that I can use it in my code. 376
376
So let's go down here or actually it's right here. 377
377
So you'll see the API path is actually exactly the same 378
378
as for getting all the recipes. 379
379
Now, the only difference is that of course, 380
380
we will not a search for anything now 381
381
but we will have to add our key like this, 382
382
so with this key parameter, okay. 383
383
And so yeah, we are doing a post request now, right. 384
384
And so that creates a new recipe 385
385
then when we hit this end point, 386
386
so this path with a post request and some data, okay. 387
387
Now, let's store actually this key in the config file. 388
388
So as a string, okay. 389
389
And then let's use that key here. 390
390
So again, the question mark to specify a list of parameters 391
391
and then key equals, 392
392
and then we have to import it up here 393
393
and then the key, okay. 394
394
And remember that this will actually send the recipe 395
395
then back to us. 396
396
And so let's store that as data and also await it. 397
397
And then finally, let's log that data to the console. 398
398
So console.logData, and now let's see what happens. 399
399
So we got a lot of errors here, 400
400
so it seems like the data is completely wrong. 401
401
And that's simply because I just forgot 402
402
to actually put that data here. 403
403
So the recipe, so of course send JSON has two parameters. 404
404
So not only the URL but also the data. 405
405
Let's try that again. 406
406
And it kind of worked 407
407
already because this error that we got back 408
408
here is actually already an error coming from our API. 409
409
So tells us that the input data is invalid 410
410
because the source URL 411
411
needs to be at least five characters long. 412
412
So let's try that again. 413
413
And actually we need to reload for now. 414
414
So the URL, let's just add something here 415
415
and probably all of them need this. 416
416
So let's do that quickly here in the HTML. 417
417
So I say, default value kind of, 418
418
so you see now we have test 23. 419
419
And as we upload it now then that worked. 420
420
So we get a status success back from our server 421
421
and then our data, right. 422
422
So that data contains everything that we just send 423
423
it plus the created at and also an ID. 424
424
And of course also your key 425
425
and that key will be very important in the next video 426
426
when we then want to mark our own recipes, 427
427
basically as our own. 428
428
Now, it will make sense to actually render 429
429
this newly created recipe also then to the user interface 430
430
after closing this display here, 431
431
so after closing this window, right. 432
432
And so therefore we need to convert 433
433
that to the format that we use in our application, right. 434
434
So basically in our application 435
435
we are using these different property names, right. 436
436
So we are in the same situation 437
437
as we are here in load recipe 438
438
where we get the data in the original format. 439
439
So with this kind of property names, 440
440
and we now need to convert it back to this one 441
441
so that all application understands the data. 442
442
All right, now, instead of writing this entire code 443
443
here twice, we can simply grab 444
444
this and put it into a new function. 445
445
So let's do that right here and call that one, 446
446
create recipe object, 447
447
and it simply receives some data. 448
448
And then it returns an object like this. 449
449
So return this soda then we can do whatever we want with it. 450
450
And in this case, 451
451
that's going to be to put that in the state. 452
452
So state.recipe will be create recipe object 453
453
with the data that we receive. 454
454
So basically just some nice refactoring. 455
455
So this now does exactly what we had before, 456
456
but now we can use that down here as well. 457
457
So we get the data 458
458
and now we want to then store that into the state. 459
459
So again, state.recipe equals create recipe object 460
460
with the data that we just received. 461
461
And now, let's actually come here to the controller 462
462
and log that stated recipe to the console, 463
463
so that's model.state.recipe. 464
464
Okay, so let's see. 465
465
And yeah, it now looks exactly the same as this one, right? 466
466
So beautiful. 467
467
There's just one thing missing, 468
468
which is this bookmarked here and also the key 469
469
is missing as well. 470
470
So let's take care of the bookmarking 471
471
and also of preserving our API key in this object, okay. 472
472
And a quick look at our flow chart 473
473
here will reveal that all we want to do is basically store 474
474
the bookmarks to local storage, right. 475
475
And also mark, of course, that recipe as a bookmark. 476
476
Okay, so we already implemented most of this. 477
477
So this and this in the last lecture, 478
478
then this part and this part in this lecture, 479
479
but let's now also take care of bookmarking it, 480
480
but that is very easy 481
481
because we already have the function for that. 482
482
So that is of course, add bookmark. 483
483
So we already have our recipe. 484
484
And so we can simply call add bookmark with our recipe 485
485
and that will then take care of everything. 486
486
So add bookmark with state.recipe. 487
487
And finally, we also need to add our key to the object, 488
488
so to state.recipe. 489
489
And we could do that here manually, 490
490
but let's actually use a nice trick 491
491
to do that down here or up here 492
492
where the object is actually created, so right here. 493
493
Now, in the case of a load recipe, 494
494
not always there will be a key. 495
495
So most of the recipes of course, don't have any key. 496
496
And so we cannot simply do this. 497
497
So key recipe.key because again, 498
498
not all of them will have one. 499
499
So we only want to add the key if there actually exists one. 500
500
So let's use a nice trick to do that. 501
501
So we can use the end operator, which does short-circuiting. 502
502
So we can do a recipe.key and then this object, all right. 503
503
I will explain to you in a second what this does that. 504
504
And then we can spread this. 505
505
So that looks confusing. 506
506
So let me explain what is happening here. 507
507
So remember that the end operator short-circuits. 508
508
So if recipe.key is a faulty value, 509
509
so if it doesn't exist 510
510
well, then nothing happens here, right. 511
511
And so then destructuring here, well does basically nothing. 512
512
Now, if this actually is some value, 513
513
then the second part of the operator 514
514
is executed and returned. 515
515
And so in that case, it is this object 516
516
here basically that is going to be returned. 517
517
And so then this whole expression will become that object. 518
518
And so then we can spread that object 519
519
to basically put the values here. 520
520
And so that will then be the same 521
521
as if the values would be out here like this, 522
522
key recipe.key. 523
523
But again, only in case that the key actually does exist. 524
524
And so this is a very nice trick 525
525
to conditionally add properties to an object. 526
526
So keep this one in mind, 527
527
now it is a very handy trick sometimes. 528
528
Now, okay, and I think we should actually be done here now. 529
529
So you see uploading data is a lot of work, 530
530
but I think this is still a nice exercise and let's see. 531
531
Here is our key now. 532
532
And also the recipe is now bookmarked, great. 533
533
So just to finish this lecture very quickly, 534
534
let's go back to the controller 535
535
and now actually render this recipe in the recipe view. 536
536
So render recipe, which will be recipeView.render, 537
537
and then as always model.state.recipe. 538
538
But now with this, 539
539
we would not really be able to see that rendered recipe yet 540
540
because we still have that form open. 541
541
So let's now close that form. 542
542
So close form window. 543
543
And we can do that because there is already a method 544
544
for that here. 545
545
So it's this public toggle window method. 546
546
And so let's now use that, okay. 547
547
So and actually let's not do that immediately, 548
548
but only after some time so that we can first display 549
549
a nice success message. 550
550
So we can use a set timeout and an insight of that 551
551
callback function. 552
552
We can then call, addRecipeView.toggleWindow, 553
553
and here let's specify some time, 554
554
let's say two and a half seconds. 555
555
However, as you already know, 556
556
we should never use a magic number like this. 557
557
So let's create one more configuration variable here, 558
558
let's call this the modal close, 559
559
and then let's say seconds so that we know that this value 560
560
is actually a value in seconds. 561
561
So let's use two and a half. 562
562
And now, we need to actually import 563
563
this because I believe that right now 564
564
we are not yet importing this into the controller. 565
565
And indeed we're not. 566
566
So let's say that the named import from config.js. 567
567
And so then down here let's use that modal close seconds 568
568
and then times a thousand 569
569
to convert it to milliseconds, okay. 570
570
And before we do that let's also display a success message. 571
571
And for that we already have in our view, 572
572
the render message method, right. 573
573
Which will take this.message, 574
574
which in this case we don't have one yet, 575
575
but let's actually add one. 576
576
Recipe was successfully uploaded. 577
577
And so now it's very, very easy to do this. 578
578
All we have to do is addRecipeView.renderMessage. 579
579
And so once again, 580
580
now that we have all of these nice methods in place, 581
581
it's very easy to take data 582
582
and then render all kinds of things on the user interface. 583
583
For example, like this error, or the success message, 584
584
or of course the data itself. 585
585
So let's try it one more time. 586
586
And there is our success message and it also disappeared. 587
587
And great, here is our recipe. 588
588
And also with our nicely formatted ingredients. 589
589
So remember we put 0.5 kilos of rice. 590
590
And so of course, that was successfully converted to an half 591
591
And you see it's also already a bookmark. 592
592
So you see that it's right here. 593
593
Of course, there is no image 594
594
because that path that I specified 595
595
there is of course not a correct path 596
596
but that doesn't matter at all here. 597
597
And now, to really finish 598
598
let's now add just one final thing 599
599
which is to render a loading spinner 600
600
in the add recipe view, 601
601
before we actually start to upload the data. 602
602
So this will then show the user that something is happening. 603
603
And so one more time that is now really easy to do 604
604
because we already have all these methods in place. 605
605
Okay, let's slow down our network a little bit to slow 3D 606
606
so that we can actually see it happening. 607
607
Well, that didn't display the spinner for some reason there. 608
608
So let's see. 609
609
AddRecipeView.renderSpinner, looks pretty correct to me. 610
610
So let's just take out this piece 611
611
here so that we can actually inspect the code. 612
612
Now it's taking more time because we are, 613
613
so let's put it back online, add recipe. 614
614
Oh, and now, you see it actually showed the loading spinner. 615
615
So not sure what happened to error but now it is all good. 616
616
Okay, great. 617
617
So all we have left to do now is to then display this recipe 618
618
as our own, 619
619
basically by using the key 620
620
that we have now here in our object 621
621
and also do the same thing in the search. 622
622
So let's take maybe a quick break 623
623
and then come back to finally finish this application.
Can't find what you're looking for?
Get subtitles in any language from opensubtitles.com, and translate them here.