Skip to content

Commit

Permalink
Add Dispatchers (yewstack#639)
Browse files Browse the repository at this point in the history
* Add Dispatchers, which act like bridges, but don't require a callback to create; updated router example

* cargo fmt

* improve comment

* Another approach

* add newtype around dispatcher bridges

* added debug impl, run cargo fmt

* fix example

* make button on routing example start on loading variant

* revert singleton_id changes

* actually revert singleton_id changes

* slabs own option<callback>

* cargo fmt

* remove dead lines

* address bad doc comment

* fix router example

* fix handler id initialization in local agent

* add appropriate error message when id is not associated with callback

* remove misleading comment

* use a type alias to the shared output slab
  • Loading branch information
hgzimmerman authored and llebout committed Jan 20, 2020
1 parent 2dd60cb commit 39d0079
Show file tree
Hide file tree
Showing 4 changed files with 225 additions and 91 deletions.
2 changes: 1 addition & 1 deletion examples/routing/src/b_component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use yew::{html, Bridge, Component, ComponentLink, Html, Renderable, ShouldRender
pub struct BModel {
number: Option<usize>,
sub_path: Option<String>,
router: Box<Bridge<Router<()>>>,
router: Box<dyn Bridge<Router<()>>>,
}

pub enum Msg {
Expand Down
49 changes: 17 additions & 32 deletions examples/routing/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

mod b_component;
mod router;
mod router_button;
mod routing;
use b_component::BModel;

use crate::router_button::RouterButton;
use log::info;
use router::Route;
use yew::agent::Bridged;
Expand All @@ -14,15 +16,15 @@ pub enum Child {
A,
B,
PathNotFound(String),
Loading,
}

pub struct Model {
child: Child,
router: Box<Bridge<router::Router<()>>>,
router: Box<dyn Bridge<router::Router<()>>>,
}

pub enum Msg {
NavigateTo(Child),
HandleRoute(Route<()>),
}

Expand All @@ -32,40 +34,20 @@ impl Component for Model {

fn create(_: Self::Properties, mut link: ComponentLink<Self>) -> Self {
let callback = link.send_back(|route: Route<()>| Msg::HandleRoute(route));
let mut router = router::Router::bridge(callback);

// TODO Not sure if this is technically correct. This should be sent _after_ the component has been created.
// I think the `Component` trait should have a hook called `on_mount()`
// that is called after the component has been attached to the vdom.
// It seems like this only works because the JS engine decides to activate the
// router worker logic after the mounting has finished.
router.send(router::Request::GetCurrentRoute);

let router = router::Router::bridge(callback);
Model {
child: Child::A, // This should be quickly overwritten by the actual route.
child: Child::Loading, // This should be quickly overwritten by the actual route.
router,
}
}

fn mounted(&mut self) -> ShouldRender {
self.router.send(router::Request::GetCurrentRoute);
false
}

fn update(&mut self, msg: Self::Message) -> ShouldRender {
match msg {
Msg::NavigateTo(child) => {
let path_segments = match child {
Child::A => vec!["a".into()],
Child::B => vec!["b".into()],
Child::PathNotFound(_) => vec!["path_not_found".into()],
};

let route = router::Route {
path_segments,
query: None,
fragment: None,
state: (),
};

self.router.send(router::Request::ChangeRoute(route));
false
}
Msg::HandleRoute(route) => {
info!("Routing: {}", route.to_route_string());
// Instead of each component selecting which parts of the path are important to it,
Expand All @@ -92,8 +74,8 @@ impl Renderable<Model> for Model {
html! {
<div>
<nav class="menu">
<button onclick=|_| Msg::NavigateTo(Child::A)>{ "Go to A" }</button>
<button onclick=|_| Msg::NavigateTo(Child::B)>{ "Go to B" }</button>
<RouterButton text="Go to A" path="/a" />
<RouterButton text="Go to B" path="/b" />
</nav>
<div>
{self.child.view()}
Expand All @@ -105,7 +87,7 @@ impl Renderable<Model> for Model {

impl Renderable<Model> for Child {
fn view(&self) -> Html<Model> {
match *self {
match self {
Child::A => html! {
<>
{"This corresponds to route 'a'"}
Expand All @@ -122,6 +104,9 @@ impl Renderable<Model> for Child {
{format!("Invalid path: '{}'", path)}
</>
},
Child::Loading => html! {
{"Loading"}
},
}
}
}
81 changes: 81 additions & 0 deletions examples/routing/src/router_button.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
//! A component wrapping a <button/> tag that changes the route.
use crate::router::Route;
use crate::router::Router;
use yew::agent::Dispatched;
use yew::prelude::*;

use crate::router::Request;
use yew::agent::Dispatcher;

/// Changes the route when clicked.
pub struct RouterButton {
router: Dispatcher<Router<()>>,
props: Props,
}

pub enum Msg {
Clicked,
}

/// Properties for Routing Components
#[derive(Properties, Default, Clone, Debug, PartialEq)]
pub struct Props {
/// The route that will be set when the component is clicked.
pub path: String,
/// The text to display.
pub text: String,
/// Disable the component.
pub disabled: bool,
/// Classes to be added to component.
pub classes: String,
}

impl Component for RouterButton {
type Message = Msg;
type Properties = Props;

fn create(props: Self::Properties, _link: ComponentLink<Self>) -> Self {
let router = Router::dispatcher();

RouterButton { router, props }
}

fn update(&mut self, msg: Self::Message) -> ShouldRender {
match msg {
Msg::Clicked => {
let route = Route {
path_segments: self
.props
.path
.split("/")
.skip(1)
.map(|s| s.to_string())
.collect::<Vec<_>>(),
query: None,
state: (),
fragment: None,
};
self.router.send(Request::ChangeRoute(route));
false
}
}
}
fn change(&mut self, props: Self::Properties) -> ShouldRender {
self.props = props;
true
}
}

impl Renderable<RouterButton> for RouterButton {
fn view(&self) -> Html<RouterButton> {
html! {
<button
class=self.props.classes.clone(),
onclick=|_| Msg::Clicked,
disabled=self.props.disabled,
>
{&self.props.text}
</button>
}
}
}
Loading

0 comments on commit 39d0079

Please sign in to comment.