@@ -224,9 +224,9 @@ We create a simple `struct` (in `main.rs` or any file of our choice) that will h
224224#[derive(Clone , PartialEq )]
225225struct Video {
226226 id : usize ,
227- title : String ,
228- speaker : String ,
229- url : String ,
227+ title : AttrValue ,
228+ speaker : AttrValue ,
229+ url : AttrValue ,
230230}
231231```
232232
@@ -238,52 +238,35 @@ fn app() -> Html {
238238+ let videos = vec! [
239239+ Video {
240240+ id : 1 ,
241- + title : " Building and breaking things" . to_string (),
242- + speaker : " John Doe" . to_string (),
243- + url : " https://youtu.be/PsaFVLr8t4E" . to_string (),
241+ + title : " Building and breaking things" . into (),
242+ + speaker : " John Doe" . into (),
243+ + url : " https://youtu.be/PsaFVLr8t4E" . into (),
244244+ },
245245+ Video {
246246+ id : 2 ,
247- + title : " The development process" . to_string (),
248- + speaker : " Jane Smith" . to_string (),
249- + url : " https://youtu.be/PsaFVLr8t4E" . to_string (),
247+ + title : " The development process" . into (),
248+ + speaker : " Jane Smith" . into (),
249+ + url : " https://youtu.be/PsaFVLr8t4E" . into (),
250250+ },
251251+ Video {
252252+ id : 3 ,
253- + title : " The Web 7.0" . to_string (),
254- + speaker : " Matt Miller" . to_string (),
255- + url : " https://youtu.be/PsaFVLr8t4E" . to_string (),
253+ + title : " The Web 7.0" . into (),
254+ + speaker : " Matt Miller" . into (),
255+ + url : " https://youtu.be/PsaFVLr8t4E" . into (),
256256+ },
257257+ Video {
258258+ id : 4 ,
259- + title : " Mouseless development" . to_string (),
260- + speaker : " Tom Jerry" . to_string (),
261- + url : " https://youtu.be/PsaFVLr8t4E" . to_string (),
259+ + title : " Mouseless development" . into (),
260+ + speaker : " Tom Jerry" . into (),
261+ + url : " https://youtu.be/PsaFVLr8t4E" . into (),
262262+ },
263263+ ];
264264+
265265```
266266
267- To display them, we need to convert the ` Vec ` into ` Html ` . We can do that by creating an iterator,
268- mapping it to ` html! ` and collecting it as ` Html ` :
267+ To display them, we can use a ` for ` loop right in the macro in place of the hardcoded HTML:
269268
270- ``` rust {4-7}
271- },
272- ];
273-
274- + let videos = videos . iter (). map (| video | html! {
275- + <p key = {video . id}>{format! (" {}: {}" , video . speaker, video . title)}</ p >
276- + }). collect :: <Html >();
277- +
278- ```
279-
280- :::tip
281- Keys on list items help Yew keep track of which items have changed in the list, resulting in faster re-renders. [ It is always recommended to use keys in lists] ( /concepts/html/lists.mdx#keyed-lists ) .
282- :::
283-
284- And finally, we need to replace the hardcoded list of videos with the ` Html ` we created from the data:
285-
286- ``` rust {6-10}
269+ ``` rust {6-12}
287270 html! {
288271 <>
289272 <h1 >{ " RustConf Explorer" }</ h1 >
@@ -293,13 +276,20 @@ And finally, we need to replace the hardcoded list of videos with the `Html` we
293276- <p >{ " Jane Smith: The development process" }</ p >
294277- <p >{ " Matt Miller: The Web 7.0" }</ p >
295278- <p >{ " Tom Jerry: Mouseless development" }</ p >
296- + { videos }
279+ + for video in & videos {
280+ + <p key = {video . id}>{format! (" {}: {}" , video . speaker, video . title)}</ p >
281+ + }
297282 </ div >
298283 // ...
299284 </ >
300285 }
301286```
302287
288+ :::tip
289+ Keys on list items help Yew keep track of which items have changed in the list, resulting in faster re-renders.
290+ [ It is always recommended to use keys in lists] ( /concepts/html/lists.mdx#keyed-lists ) .
291+ :::
292+
303293## Components
304294
305295Components are the building blocks of Yew applications. By combining components, which can be made of other components,
@@ -325,9 +315,11 @@ struct VideosListProps {
325315
326316#[function_component(VideosList )]
327317fn videos_list (VideosListProps { videos }: & VideosListProps ) -> Html {
328- videos . iter (). map (| video | html! {
329- <p key = {video . id}>{format! (" {}: {}" , video . speaker, video . title)}</ p >
330- }). collect ()
318+ html! {
319+ for video in videos {
320+ <p key = {video . id}>{format! (" {}: {}" , video . speaker, video . title)}</ p >
321+ }
322+ }
331323}
332324```
333325
@@ -345,17 +337,15 @@ Now, we can update our `App` component to make use of `VideosList` component.
345337#[function_component(App )]
346338fn app () -> Html {
347339 // ...
348- - let videos = videos . iter (). map (| video | html! {
349- - <p key = {video . id}>{format! (" {}: {}" , video . speaker, video . title)}</ p >
350- - }). collect :: <Html >();
351- -
352340 html! {
353341 <>
354342 <h1 >{ " RustConf Explorer" }</ h1 >
355343 <div >
356344 <h3 >{ " Videos to watch" }</ h3 >
357- - { videos }
358- + <VideosList videos = {videos } / >
345+ - for video in & videos {
346+ - <p key = {video . id}>{format! (" {}: {}" , video . speaker, video . title)}</ p >
347+ - }
348+ + <VideosList {videos } / >
359349 </ div >
360350 // ...
361351 </ >
@@ -387,22 +377,20 @@ Then we modify the `VideosList` component to "emit" the selected video to the ca
387377#[function_component(VideosList )]
388378- fn videos_list (VideosListProps { videos }: & VideosListProps ) -> Html {
389379+ fn videos_list (VideosListProps { videos , on_click }: & VideosListProps ) -> Html {
390- - videos . iter (). map (| video | html! {
391- - <p key = {video . id}>{format! (" {}: {}" , video . speaker, video . title)}</ p >
392- + let on_click = on_click . clone ();
393- + videos . iter (). map (| video | {
394- + let on_video_select = {
395- + let on_click = on_click . clone ();
396- + let video = video . clone ();
397- + Callback :: from (move | _ | {
398- + on_click . emit (video . clone ())
399- + })
400- + };
380+ + let on_select = | video : & Video | {
381+ + let on_click = on_click . clone ();
382+ + let video = video . clone ();
383+ + Callback :: from (move | _ | {
384+ + on_click . emit (video . clone ())
385+ + })
386+ + };
401387+
402- + html! {
403- + <p key = {video . id} onclick = {on_video_select }>{format! (" {}: {}" , video . speaker, video . title)}</ p >
404- + }
405- }). collect ()
388+ html! {
389+ for video in videos {
390+ - <p key = {video . id}>{format! (" {}: {}" , video . speaker, video . title)}</ p >
391+ + <p key = {video . id} onclick = {on_select (video )}>{format! (" {}: {}" , video . speaker, video . title)}</ p >
392+ }
393+ }
406394}
407395```
408396
@@ -440,20 +428,18 @@ Now, modify the `App` component to display `VideoDetails` component whenever a v
440428+ selected_video . set (Some (video ))
441429+ })
442430+ };
443- +
444- + let details = selected_video . as_ref (). map (| video | html! {
445- + <VideoDetails video = {video . clone ()} / >
446- + });
447431
448432 html! {
449433 <>
450434 <h1 >{ " RustConf Explorer" }</ h1 >
451435 <div >
452436 <h3 >{ " Videos to watch" }</ h3 >
453- - <VideosList videos = {videos } / >
454- + <VideosList videos = {videos } on_click = {on_video_select . clone ()} / >
437+ - <VideosList {videos } / >
438+ + <VideosList {videos } on_click = {on_video_select . clone ()} / >
455439 </ div >
456- + { for details }
440+ + if let Some (video ) = & * selected_video {
441+ + <VideoDetails video = {video . clone ()} / >
442+ + }
457443- <div >
458444- <h3 >{ " John Doe: Building and breaking things" }</ h3 >
459445- <img src = " https://placehold.co/640x360.png?text=Video+Player+Placeholder" alt = " video thumbnail" / >
@@ -463,11 +449,6 @@ Now, modify the `App` component to display `VideoDetails` component whenever a v
463449}
464450```
465451
466- Do not worry about the ` use_state ` right now, we will come back to that later.
467- Note the trick we pulled with ` { for details } ` . ` Option<_> ` implements ` Iterator ` so we can use it to display the only
468- element returned by the ` Iterator ` with a special ` { for ... } ` syntax
469- [ supported by the ` html! ` macro] ( concepts/html/lists ) .
470-
471452### Handling state
472453
473454Remember the ` use_state ` used earlier? That is a special function, called a "hook". Hooks are used to "hook" into
@@ -492,13 +473,18 @@ videos list from an external source. For this we will need to add the following
492473
493474Let's update the dependencies in ` Cargo.toml ` file:
494475
495- ``` toml title="Cargo.toml"
476+ ``` toml title="Cargo.toml" {2-6}
496477[dependencies ]
497- gloo-net = " 0.6"
498- serde = { version = " 1.0" , features = [" derive" ] }
499- wasm-bindgen-futures = " 0.4"
478+ -yew = { git = " https://github.com/yewstack/yew/" , features = [" csr" ] }
479+ +yew = { git = " https://github.com/yewstack/yew/" , features = [" csr" , " serde" ] }
480+ +gloo-net = "0.6"
481+ +serde = { version = " 1.0" , features = [" derive" ] }
482+ +wasm-bindgen-futures = "0.4"
500483```
501484
485+ Yew's ` serde ` feature enables integration with the ` serde ` crate, the important point for us is that
486+ it adds a ` serde::Deserialize ` impl to ` AttrValue ` .
487+
502488:::note
503489When choosing dependencies make sure they are ` wasm32 ` compatible!
504490Otherwise you won't be able to run your application.
@@ -514,9 +500,9 @@ use yew::prelude::*;
514500+ #[derive(Clone , PartialEq , Deserialize )]
515501struct Video {
516502 id : usize ,
517- title : String ,
518- speaker : String ,
519- url : String ,
503+ title : AttrValue ,
504+ speaker : AttrValue ,
505+ url : AttrValue ,
520506}
521507```
522508
@@ -531,27 +517,27 @@ fn app() -> Html {
531517- let videos = vec! [
532518- Video {
533519- id : 1 ,
534- - title : " Building and breaking things" . to_string (),
535- - speaker : " John Doe" . to_string (),
536- - url : " https://youtu.be/PsaFVLr8t4E" . to_string (),
520+ - title : " Building and breaking things" . into (),
521+ - speaker : " John Doe" . into (),
522+ - url : " https://youtu.be/PsaFVLr8t4E" . into (),
537523- },
538524- Video {
539525- id : 2 ,
540- - title : " The development process" . to_string (),
541- - speaker : " Jane Smith" . to_string (),
542- - url : " https://youtu.be/PsaFVLr8t4E" . to_string (),
526+ - title : " The development process" . into (),
527+ - speaker : " Jane Smith" . into (),
528+ - url : " https://youtu.be/PsaFVLr8t4E" . into (),
543529- },
544530- Video {
545531- id : 3 ,
546- - title : " The Web 7.0" . to_string (),
547- - speaker : " Matt Miller" . to_string (),
548- - url : " https://youtu.be/PsaFVLr8t4E" . to_string (),
532+ - title : " The Web 7.0" . into (),
533+ - speaker : " Matt Miller" . into (),
534+ - url : " https://youtu.be/PsaFVLr8t4E" . into (),
549535- },
550536- Video {
551537- id : 4 ,
552- - title : " Mouseless development" . to_string (),
553- - speaker : " Tom Jerry" . to_string (),
554- - url : " https://youtu.be/PsaFVLr8t4E" . to_string (),
538+ - title : " Mouseless development" . into (),
539+ - speaker : " Tom Jerry" . into (),
540+ - url : " https://youtu.be/PsaFVLr8t4E" . into (),
555541- },
556542- ];
557543-
@@ -581,10 +567,10 @@ fn app() -> Html {
581567 <h1 >{ " RustConf Explorer" }</ h1 >
582568 <div >
583569 <h3 >{ " Videos to watch" }</ h3 >
584- - <VideosList videos = {videos } on_click = {on_video_select . clone ()} / >
570+ - <VideosList {videos } on_click = {on_video_select . clone ()} / >
585571+ <VideosList videos = {(* videos ). clone ()} on_click = {on_video_select . clone ()} / >
586572 </ div >
587- { for details }
573+ // ...
588574 </ >
589575 }
590576}
0 commit comments