Skip to content

Commit b871142

Browse files
updated the tutorial to use more novel Yew HTML
1 parent 4f3b85e commit b871142

File tree

2 files changed

+79
-93
lines changed

2 files changed

+79
-93
lines changed

tools/website-test/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ serde = { version = "1.0", features = ["derive"] }
1818
wasm-bindgen = "0.2"
1919
wasm-bindgen-futures = "0.4"
2020
weblog = "0.3.0"
21-
yew = { path = "../../packages/yew/", features = ["ssr", "csr"] }
21+
yew = { path = "../../packages/yew/", features = ["ssr", "csr", "serde"] }
2222
yew-autoprops = "0.4.1"
2323
yew-router = { path = "../../packages/yew-router/" }
2424
tokio = { version = "1.43.1", features = ["rt", "macros"] }

website/docs/tutorial/index.mdx

Lines changed: 78 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -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)]
225225
struct 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

305295
Components 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)]
327317
fn 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)]
346338
fn 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

473454
Remember 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

493474
Let'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
503489
When choosing dependencies make sure they are `wasm32` compatible!
504490
Otherwise you won't be able to run your application.
@@ -514,9 +500,9 @@ use yew::prelude::*;
514500
+#[derive(Clone, PartialEq, Deserialize)]
515501
struct 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

Comments
 (0)