yew_stdweb/
app.rs

1//! This module contains the `App` struct, which is used to bootstrap
2//! a component in an isolated scope.
3
4use crate::html::{Component, ComponentLink, NodeRef, Scope};
5use crate::utils::document;
6use cfg_if::cfg_if;
7cfg_if! {
8    if #[cfg(feature = "std_web")] {
9        use stdweb::web::{Element, INode, IParentNode};
10    } else if #[cfg(feature = "web_sys")] {
11        use web_sys::Element;
12    }
13}
14
15/// An instance of an application.
16#[derive(Debug)]
17pub struct App<COMP: Component> {
18    /// `Scope` holder
19    scope: Scope<COMP>,
20}
21
22impl<COMP> Default for App<COMP>
23where
24    COMP: Component,
25{
26    fn default() -> Self {
27        App::new()
28    }
29}
30
31impl<COMP> App<COMP>
32where
33    COMP: Component,
34    COMP::Properties: Default,
35{
36    /// The main entry point of a Yew program. It works similarly to the `program`
37    /// function in Elm. You should provide an initial model, `update` function
38    /// which will update the state of the model and a `view` function which
39    /// will render the model to a virtual DOM tree. If you would like to pass props,
40    /// use the `mount_with_props` method.
41    pub fn mount(self, element: Element) -> ComponentLink<COMP> {
42        clear_element(&element);
43        self.scope.mount_in_place(
44            element,
45            NodeRef::default(),
46            NodeRef::default(),
47            COMP::Properties::default(),
48        )
49    }
50
51    /// Alias to `mount("body", ...)`.
52    pub fn mount_to_body(self) -> ComponentLink<COMP> {
53        // Bootstrap the component for `Window` environment only (not for `Worker`)
54        let element = document()
55            .query_selector("body")
56            .expect("can't get body node for rendering")
57            .expect("can't unwrap body node");
58        self.mount(element)
59    }
60
61    /// Alternative to `mount` which replaces the body element with a component which has a body
62    /// element at the root of the HTML generated by its `view` method. Use this method when you
63    /// need to manipulate the body element. For example, adding/removing app-wide
64    /// CSS classes of the body element.
65    pub fn mount_as_body(self) -> ComponentLink<COMP> {
66        let html_element = document()
67            .query_selector("html")
68            .expect("can't get html node for rendering")
69            .expect("can't unwrap html node");
70        let body_element = document()
71            .query_selector("body")
72            .expect("can't get body node for rendering")
73            .expect("can't unwrap body node");
74        html_element
75            .remove_child(&body_element)
76            .expect("can't remove body child");
77        self.scope.mount_in_place(
78            html_element,
79            NodeRef::default(),
80            NodeRef::default(),
81            COMP::Properties::default(),
82        )
83    }
84}
85
86impl<COMP> App<COMP>
87where
88    COMP: Component,
89{
90    /// Creates a new `App` with a component in a context.
91    pub fn new() -> Self {
92        let scope = Scope::new(None);
93        App { scope }
94    }
95
96    /// The main entry point of a Yew program which also allows passing properties. It works
97    /// similarly to the `program` function in Elm. You should provide an initial model, `update`
98    /// function which will update the state of the model and a `view` function which
99    /// will render the model to a virtual DOM tree.
100    pub fn mount_with_props(
101        self,
102        element: Element,
103        props: COMP::Properties,
104    ) -> ComponentLink<COMP> {
105        clear_element(&element);
106        self.scope
107            .mount_in_place(element, NodeRef::default(), NodeRef::default(), props)
108    }
109
110    /// Alias to `mount_with_props("body", ...)`.
111    pub fn mount_to_body_with_props(self, props: COMP::Properties) -> ComponentLink<COMP> {
112        // Bootstrap the component for `Window` environment only (not for `Worker`)
113        let element = document()
114            .query_selector("body")
115            .expect("can't get body node for rendering")
116            .expect("can't unwrap body node");
117        self.mount_with_props(element, props)
118    }
119
120    /// Alternative to `mount_with_props` which replaces the body element with a component which
121    /// has a body element at the root of the HTML generated by its `view` method. Use this method
122    /// when you need to manipulate the body element. For example, adding/removing app-wide
123    /// CSS classes of the body element.
124    pub fn mount_as_body_with_props(self, props: COMP::Properties) -> ComponentLink<COMP> {
125        let html_element = document()
126            .query_selector("html")
127            .expect("can't get html node for rendering")
128            .expect("can't unwrap html node");
129        let body_element = document()
130            .query_selector("body")
131            .expect("can't get body node for rendering")
132            .expect("can't unwrap body node");
133        html_element
134            .remove_child(&body_element)
135            .expect("can't remove body child");
136        self.scope
137            .mount_in_place(html_element, NodeRef::default(), NodeRef::default(), props)
138    }
139}
140
141/// Removes anything from the given element.
142fn clear_element(element: &Element) {
143    while let Some(child) = element.last_child() {
144        element.remove_child(&child).expect("can't remove a child");
145    }
146}