Skip to content

Commit 785da7d

Browse files
committed
feat, examples: Request::controller; server_integration - new Fetch API; IntoNodes for Options
1 parent d7c667e commit 785da7d

File tree

10 files changed

+120
-151
lines changed

10 files changed

+120
-151
lines changed

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@
3737
- [BREAKING] `Node::add_listener` renamed to `add_event_handler`.
3838
- Rewritten `README.md`.
3939
- Added new Fetch API module. See [`seed::browser::fetch`](https://docs.rs/seed/0.7.0/seed/browser/fetch/index.html) (#353)
40-
- [deprecated] - `seed::browser::service::fetch` module is deprecated in favor of `seed::browser::fetch`
40+
- [deprecated] - `seed::browser::service::fetch` module is deprecated in favor of `seed::browser::fetch`.
41+
- Added example `fetch`.
42+
- Implemented `IntoNodes` for `Option<Node<Msg>>` and `Option<Vec<Node<Msg>>>`.
4143

4244
## v0.6.0
4345
- Implemented `UpdateEl` for `Filter` and `FilterMap`.

examples/fetch/src/simple.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,9 @@ pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
4343
pub fn view(model: &Model) -> Node<Msg> {
4444
div![
4545
button![ev(Ev::Click, |_| Msg::Fetch), "Fetch user"],
46-
if let Some(user) = &model.user {
47-
div![format!("User: {}", user.name)]
48-
} else {
49-
empty![]
50-
}
46+
model
47+
.user
48+
.as_ref()
49+
.map(|user| div![format!("User: {}", user.name)])
5150
]
5251
}

examples/server_integration/client/src/example_a.rs

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use seed::browser::service::fetch;
21
use seed::{self, prelude::*, *};
32
use std::borrow::Cow;
43

@@ -30,7 +29,7 @@ pub struct Model {
3029
pub enum Msg {
3130
NewMessageChanged(String),
3231
SendRequest,
33-
Fetched(fetch::ResponseDataResult<shared::SendMessageResponseBody>),
32+
Fetched(fetch::Result<shared::SendMessageResponseBody>),
3433
}
3534

3635
pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
@@ -39,28 +38,29 @@ pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
3938
model.new_message = message;
4039
}
4140
Msg::SendRequest => {
42-
orders
43-
.skip()
44-
.perform_cmd(send_request(model.new_message.clone()));
41+
orders.skip().perform_cmd({
42+
let message = model.new_message.clone();
43+
async { Msg::Fetched(send_message(message).await) }
44+
});
4545
}
4646

4747
Msg::Fetched(Ok(response_data)) => {
4848
model.response_data = Some(response_data);
4949
}
5050

51-
Msg::Fetched(Err(fail_reason)) => {
52-
log!("Example_A error:", fail_reason);
51+
Msg::Fetched(Err(fetch_error)) => {
52+
log!("Example_A error:", fetch_error);
5353
orders.skip();
5454
}
5555
}
5656
}
5757

58-
async fn send_request(new_message: String) -> Msg {
59-
fetch::Request::new(get_request_url())
60-
.method(fetch::Method::Post)
61-
.send_json(&shared::SendMessageRequestBody { text: new_message })
62-
.fetch_json_data(Msg::Fetched)
63-
.await
58+
async fn send_message(new_message: String) -> fetch::Result<shared::SendMessageResponseBody> {
59+
let request = Request::new(get_request_url())
60+
.method(Method::Post)
61+
.json(&shared::SendMessageRequestBody { text: new_message })?;
62+
63+
fetch(request).await?.check_status()?.json().await
6464
}
6565

6666
// ------ ------
Lines changed: 15 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
use seed::browser::service::fetch;
21
use seed::{prelude::*, *};
32
use serde::Deserialize;
43
use std::borrow::Cow;
54

65
pub const TITLE: &str = "Example B";
76
pub const DESCRIPTION: &str =
87
"Click button 'Try to Fetch JSON' to send request to non-existent endpoint.
9-
Server will return 404 with empty body and Serde then fail to decode body into predefined JSON.";
8+
Server will return status 404 with empty body. `Response::check_status` then return error.";
109

1110
fn get_request_url() -> impl Into<Cow<'static, str>> {
1211
"/api/non-existent-endpoint"
@@ -18,10 +17,10 @@ fn get_request_url() -> impl Into<Cow<'static, str>> {
1817

1918
#[derive(Default)]
2019
pub struct Model {
21-
pub response_with_data_result: Option<fetch::ResponseWithDataResult<ExpectedResponseData>>,
20+
pub fetch_result: Option<fetch::Result<ExpectedResponseData>>,
2221
}
2322

24-
#[derive(Debug, Deserialize, Clone)]
23+
#[derive(Debug, Deserialize)]
2524
pub struct ExpectedResponseData {
2625
something: String,
2726
}
@@ -32,47 +31,36 @@ pub struct ExpectedResponseData {
3231

3332
pub enum Msg {
3433
SendRequest,
35-
Fetched(fetch::FetchResult<ExpectedResponseData>),
34+
Fetched(fetch::Result<ExpectedResponseData>),
3635
}
3736

3837
pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
3938
match msg {
4039
Msg::SendRequest => {
41-
orders.skip().perform_cmd(send_request());
40+
orders.skip().perform_cmd(async {
41+
Msg::Fetched(
42+
async { fetch(get_request_url()).await?.check_status()?.json().await }.await,
43+
)
44+
});
4245
}
4346

44-
Msg::Fetched(Ok(response_with_data_result)) => {
45-
model.response_with_data_result = Some(response_with_data_result);
46-
}
47-
48-
Msg::Fetched(Err(request_error)) => {
49-
log!("Example_B error:", request_error);
50-
orders.skip();
47+
Msg::Fetched(fetch_result) => {
48+
model.fetch_result = Some(fetch_result);
5149
}
5250
}
5351
}
5452

55-
async fn send_request() -> Msg {
56-
fetch::Request::new(get_request_url())
57-
.fetch_json(|fetch_object| Msg::Fetched(fetch_object.result))
58-
.await
59-
}
60-
6153
// ------ ------
6254
// View
6355
// ------ ------
6456

6557
pub fn view(model: &Model, intro: impl FnOnce(&str, &str) -> Vec<Node<Msg>>) -> Vec<Node<Msg>> {
6658
nodes![
6759
intro(TITLE, DESCRIPTION),
68-
match &model.response_with_data_result {
69-
None => empty![],
70-
Some(fetch::ResponseWithDataResult { status, data, .. }) => div![
71-
div![format!("Status code: {}", status.code)],
72-
div![format!(r#"Status text: "{}""#, status.text)],
73-
div![format!(r#"Data: "{:#?}""#, data)]
74-
],
75-
},
60+
model
61+
.fetch_result
62+
.as_ref()
63+
.map(|result| div![format!("{:#?}", result)]),
7664
button![ev(Ev::Click, |_| Msg::SendRequest), "Try to Fetch JSON"],
7765
]
7866
}
Lines changed: 25 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use seed::browser::service::fetch;
1+
// use seed::browser::service::fetch;
22
use seed::{prelude::*, *};
33
use std::borrow::Cow;
44

@@ -18,7 +18,7 @@ fn get_request_url() -> impl Into<Cow<'static, str>> {
1818

1919
#[derive(Default)]
2020
pub struct Model {
21-
pub response_data_result: Option<fetch::ResponseDataResult<String>>,
21+
pub fetch_result: Option<fetch::Result<String>>,
2222
pub request_controller: Option<fetch::RequestController>,
2323
pub status: Status,
2424
}
@@ -42,31 +42,31 @@ impl Default for Status {
4242
pub enum Msg {
4343
SendRequest,
4444
AbortRequest,
45-
Fetched(fetch::ResponseDataResult<String>),
45+
Fetched(fetch::Result<String>),
4646
}
4747

4848
pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
4949
match msg {
5050
Msg::SendRequest => {
51+
let (request, controller) = Request::new(get_request_url()).controller();
5152
model.status = Status::WaitingForResponse;
52-
model.response_data_result = None;
53-
let request = fetch::Request::new(get_request_url())
54-
.controller(|controller| model.request_controller = Some(controller));
55-
orders.perform_cmd(request.fetch_string_data(Msg::Fetched));
53+
model.fetch_result = None;
54+
model.request_controller = Some(controller);
55+
orders.perform_cmd(async {
56+
Msg::Fetched(async { fetch(request).await?.text().await }.await)
57+
});
5658
}
5759

5860
Msg::AbortRequest => {
59-
model
60-
.request_controller
61-
.take()
62-
.expect("AbortRequest: request_controller hasn't been set!")
63-
.abort();
61+
if let Some(controller) = &model.request_controller {
62+
controller.abort();
63+
}
6464
model.status = Status::RequestAborted;
6565
}
6666

67-
Msg::Fetched(response_data_result) => {
67+
Msg::Fetched(fetch_result) => {
6868
model.status = Status::ReadyToSendRequest;
69-
model.response_data_result = Some(response_data_result);
69+
model.fetch_result = Some(fetch_result);
7070
}
7171
}
7272
}
@@ -79,16 +79,22 @@ pub fn view(model: &Model, intro: impl FnOnce(&str, &str) -> Vec<Node<Msg>>) ->
7979
nodes![
8080
intro(TITLE, DESCRIPTION),
8181
match model.status {
82-
Status::ReadyToSendRequest => vec![
83-
view_response_data_result(&model.response_data_result),
82+
Status::ReadyToSendRequest => nodes![
83+
model
84+
.fetch_result
85+
.as_ref()
86+
.map(|result| div![format!("{:#?}", result)]),
8487
button![ev(Ev::Click, |_| Msg::SendRequest), "Send request"],
8588
],
86-
Status::WaitingForResponse => vec![
89+
Status::WaitingForResponse => nodes![
8790
div!["Waiting for response..."],
8891
button![ev(Ev::Click, |_| Msg::AbortRequest), "Abort request"],
8992
],
90-
Status::RequestAborted => vec![
91-
view_response_data_result(&model.response_data_result),
93+
Status::RequestAborted => nodes![
94+
model
95+
.fetch_result
96+
.as_ref()
97+
.map(|result| div![format!("{:#?}", result)]),
9298
button![
9399
attrs! {At::Disabled => false.as_at_value()},
94100
"Request aborted"
@@ -97,28 +103,3 @@ pub fn view(model: &Model, intro: impl FnOnce(&str, &str) -> Vec<Node<Msg>>) ->
97103
}
98104
]
99105
}
100-
101-
fn view_response_data_result(
102-
response_data_result: &Option<fetch::ResponseDataResult<String>>,
103-
) -> Node<Msg> {
104-
match &response_data_result {
105-
None => empty![],
106-
Some(Ok(response_data)) => div![format!(r#"Response String body: "{}""#, response_data)],
107-
Some(Err(fail_reason)) => view_fail_reason(fail_reason),
108-
}
109-
}
110-
111-
fn view_fail_reason(fail_reason: &fetch::FailReason<String>) -> Node<Msg> {
112-
if let fetch::FailReason::RequestError(fetch::RequestError::DomException(dom_exception), _) =
113-
fail_reason
114-
{
115-
if dom_exception.name() == "AbortError" {
116-
return div![
117-
div![format!(r#"Error name: "{}""#, dom_exception.name())],
118-
div![format!(r#"Error message: "{}""#, dom_exception.message())]
119-
];
120-
}
121-
}
122-
log!("Example_C error:", fail_reason);
123-
empty![]
124-
}

examples/server_integration/client/src/example_d.rs

Lines changed: 23 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use seed::browser::service::fetch;
21
use seed::{prelude::*, *};
32
use std::borrow::Cow;
43

@@ -20,7 +19,7 @@ fn get_request_url() -> impl Into<Cow<'static, str>> {
2019

2120
#[derive(Default)]
2221
pub struct Model {
23-
pub response_result: Option<fetch::ResponseResult<()>>,
22+
pub fetch_result: Option<fetch::Result<String>>,
2423
pub request_controller: Option<fetch::RequestController>,
2524
pub status: Status,
2625
}
@@ -48,34 +47,35 @@ impl Default for Status {
4847
pub enum Msg {
4948
SendRequest,
5049
DisableTimeout,
51-
Fetched(fetch::FetchObject<()>),
50+
Fetched(fetch::Result<String>),
5251
}
5352

5453
pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
5554
match msg {
5655
Msg::SendRequest => {
56+
let (request, controller) = Request::new(get_request_url())
57+
.timeout(TIMEOUT)
58+
.controller();
59+
5760
model.status = Status::WaitingForResponse(TimeoutStatus::Enabled);
58-
model.response_result = None;
61+
model.fetch_result = None;
62+
model.request_controller = Some(controller);
5963

60-
let request = fetch::Request::new(get_request_url())
61-
.controller(|controller| model.request_controller = Some(controller))
62-
.timeout(TIMEOUT);
63-
orders.perform_cmd(request.fetch(Msg::Fetched));
64+
orders.perform_cmd(async {
65+
Msg::Fetched(async { fetch(request).await?.text().await }.await)
66+
});
6467
}
6568

6669
Msg::DisableTimeout => {
67-
model
68-
.request_controller
69-
.take()
70-
.ok_or("Msg:DisableTimeout: request controller cannot be None")
71-
.and_then(|controller| controller.disable_timeout())
72-
.unwrap_or_else(|err| log!(err));
70+
if let Some(controller) = &model.request_controller {
71+
controller.disable_timeout().expect("disable timeout");
72+
}
7373
model.status = Status::WaitingForResponse(TimeoutStatus::Disabled)
7474
}
7575

76-
Msg::Fetched(fetch_object) => {
76+
Msg::Fetched(fetch_result) => {
7777
model.status = Status::ReadyToSendRequest;
78-
model.response_result = Some(fetch_object.response());
78+
model.fetch_result = Some(fetch_result);
7979
}
8080
}
8181
}
@@ -87,36 +87,16 @@ pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
8787
pub fn view(model: &Model, intro: impl FnOnce(&str, &str) -> Vec<Node<Msg>>) -> Vec<Node<Msg>> {
8888
nodes![
8989
intro(TITLE, DESCRIPTION),
90-
match &model.response_result {
91-
None => vec![
92-
if let Status::WaitingForResponse(_) = model.status {
93-
div!["Waiting for response..."]
94-
} else {
95-
empty![]
96-
},
97-
view_button(&model.status),
98-
],
99-
Some(Ok(response)) => vec![
100-
div![format!("Server returned {}.", response.status.text)],
101-
view_button(&model.status),
102-
],
103-
Some(Err(fail_reason)) => view_fail_reason(fail_reason, &model.status),
104-
}
90+
match &model.fetch_result {
91+
None =>
92+
IF!(matches!(model.status, Status::WaitingForResponse(_)) => div!["Waiting for response..."]),
93+
Some(Ok(result)) => Some(div![format!("Server returned: {:#?}", result)]),
94+
Some(Err(fetch_error)) => Some(div![format!("{:#?}", fetch_error)]),
95+
},
96+
view_button(&model.status),
10597
]
10698
}
10799

108-
fn view_fail_reason(fail_reason: &fetch::FailReason<()>, status: &Status) -> Vec<Node<Msg>> {
109-
if let fetch::FailReason::RequestError(fetch::RequestError::DomException(dom_exception), _) =
110-
fail_reason
111-
{
112-
if dom_exception.name() == "AbortError" {
113-
return vec![div!["Request aborted."], view_button(status)];
114-
}
115-
}
116-
log!("Example_D error:", fail_reason);
117-
vec![]
118-
}
119-
120100
pub fn view_button(status: &Status) -> Node<Msg> {
121101
match status {
122102
Status::WaitingForResponse(TimeoutStatus::Enabled) => {

0 commit comments

Comments
 (0)