This is unreleased documentation for Yew Next version.
For up-to-date documentation, see the latest version on docs.rs.

yew/html/component/
children.rs

1//! Component children module
2
3use std::fmt;
4use std::rc::Rc;
5
6use crate::html::{Html, ImplicitClone};
7use crate::utils::RcExt;
8use crate::virtual_dom::{VChild, VComp, VList, VNode};
9use crate::{BaseComponent, Properties};
10
11/// A type used for accepting children elements in Component::Properties.
12///
13/// # Example
14/// **`model.rs`**
15///
16/// In this example, the `Wrapper` component is used to wrap other elements.
17/// ```
18/// # use yew::{Children, Html, Properties, Component, Context, html};
19/// # #[derive(Clone, Properties, PartialEq)]
20/// # struct WrapperProps {
21/// #     children: Children,
22/// # }
23/// # struct Wrapper;
24/// # impl Component for Wrapper {
25/// #     type Message = ();
26/// #     type Properties = WrapperProps;
27/// #   fn create(ctx: &Context<Self>) -> Self { Self }
28/// #   fn view(&self, ctx: &Context<Self>) -> Html {
29/// html! {
30///     <Wrapper>
31///         <h4>{ "Hi" }</h4>
32///         <div>{ "Hello" }</div>
33///     </Wrapper>
34/// }
35/// #     }
36/// # }
37/// ```
38///
39/// **`wrapper.rs`**
40///
41/// The Wrapper component must define a `children` property in order to wrap other elements. The
42/// children property can be used to render the wrapped elements.
43/// ```
44/// # use yew::{Children, Html, Properties, Component, Context, html};
45/// #[derive(Clone, Properties, PartialEq)]
46/// struct WrapperProps {
47///     children: Children,
48/// }
49///
50/// # struct Wrapper;
51/// impl Component for Wrapper {
52///     // ...
53/// #     type Message = ();
54/// #     type Properties = WrapperProps;
55/// #    fn create(ctx: &Context<Self>) -> Self { Self }
56///     fn view(&self, ctx: &Context<Self>) -> Html {
57///         html! {
58///             <div id="container">
59///                 { ctx.props().children.clone() }
60///             </div>
61///         }
62///     }
63/// }
64/// ```
65pub type Children = ChildrenRenderer<Html>;
66
67/// A type used for accepting children elements in Component::Properties and accessing their props.
68///
69/// # Example
70/// **`model.rs`**
71///
72/// In this example, the `List` component can wrap `ListItem` components.
73/// ```
74/// # use yew::{html, Component, Html, Context, ChildrenWithProps, Properties};
75/// #
76/// # #[derive(Clone, Properties, PartialEq)]
77/// # struct ListProps {
78/// #     children: ChildrenWithProps<ListItem>,
79/// # }
80/// # struct List;
81/// # impl Component for List {
82/// #     type Message = ();
83/// #     type Properties = ListProps;
84/// #   fn create(ctx: &Context<Self>) -> Self { Self }
85/// #   fn view(&self, ctx: &Context<Self>) -> Html { unimplemented!() }
86/// # }
87/// # #[derive(Clone, Properties, PartialEq)]
88/// # struct ListItemProps {
89/// #     value: String
90/// # }
91/// # struct ListItem;
92/// # impl Component for ListItem {
93/// #     type Message = ();
94/// #     type Properties = ListItemProps;
95/// #   fn create(ctx: &Context<Self>) -> Self { Self }
96/// #   fn view(&self, ctx: &Context<Self>) -> Html { unimplemented!() }
97/// # }
98/// # fn view() -> Html {
99/// html! {
100///   <List>
101///     <ListItem value="a" />
102///     <ListItem value="b" />
103///     <ListItem value="c" />
104///   </List>
105/// }
106/// # }
107/// ```
108///
109/// **`list.rs`**
110///
111/// The `List` component must define a `children` property in order to wrap the list items. The
112/// `children` property can be used to filter, mutate, and render the items.
113/// ```
114/// # use yew::{html, Component, Html, ChildrenWithProps, Context, Properties};
115/// # use std::rc::Rc;
116/// #
117/// #[derive(Clone, Properties, PartialEq)]
118/// struct ListProps {
119///     children: ChildrenWithProps<ListItem>,
120/// }
121///
122/// # struct List;
123/// impl Component for List {
124/// #     type Message = ();
125/// #     type Properties = ListProps;
126/// #   fn create(ctx: &Context<Self>) -> Self { Self }
127/// #   fn view(&self, ctx: &Context<Self>) -> Html {
128///         html!{{
129///             for ctx.props().children.iter().map(|mut item| {
130///                 let mut props = Rc::make_mut(&mut item.props);
131///                 props.value = format!("item-{}", props.value);
132///                 item
133///             })
134///         }}
135///     }
136/// }
137/// #
138/// # #[derive(Clone, Properties, PartialEq)]
139/// # struct ListItemProps {
140/// #     #[prop_or_default]
141/// #     value: String
142/// # }
143/// #
144/// # struct ListItem;
145/// # impl Component for ListItem {
146/// #     type Message = ();
147/// #     type Properties = ListItemProps;
148/// #   fn create(ctx: &Context<Self>) -> Self { Self }
149/// #   fn view(&self, ctx: &Context<Self>) -> Html { unimplemented!() }
150/// # }
151/// ```
152pub type ChildrenWithProps<CHILD> = ChildrenRenderer<VChild<CHILD>>;
153
154/// A type used for rendering children html.
155pub struct ChildrenRenderer<T> {
156    pub(crate) children: Option<Rc<Vec<T>>>,
157}
158
159impl<T> Clone for ChildrenRenderer<T> {
160    fn clone(&self) -> Self {
161        Self {
162            children: self.children.clone(),
163        }
164    }
165}
166
167impl<T> ImplicitClone for ChildrenRenderer<T> {}
168
169impl<T: PartialEq> PartialEq for ChildrenRenderer<T> {
170    fn eq(&self, other: &Self) -> bool {
171        match (self.children.as_ref(), other.children.as_ref()) {
172            (Some(a), Some(b)) => a == b,
173            (Some(a), None) => a.is_empty(),
174            (None, Some(b)) => b.is_empty(),
175            (None, None) => true,
176        }
177    }
178}
179
180impl<T> ChildrenRenderer<T>
181where
182    T: Clone,
183{
184    /// Create children
185    pub fn new(children: Vec<T>) -> Self {
186        if children.is_empty() {
187            Self { children: None }
188        } else {
189            Self {
190                children: Some(Rc::new(children)),
191            }
192        }
193    }
194
195    /// Children list is empty
196    pub fn is_empty(&self) -> bool {
197        self.children.as_ref().map(|x| x.is_empty()).unwrap_or(true)
198    }
199
200    /// Number of children elements
201    pub fn len(&self) -> usize {
202        self.children.as_ref().map(|x| x.len()).unwrap_or(0)
203    }
204
205    /// Render children components and return `Iterator`
206    pub fn iter(&self) -> impl Iterator<Item = T> + '_ {
207        // clone each child lazily.
208        // This way `self.iter().next()` only has to clone a single node.
209        self.children.iter().flat_map(|x| x.iter()).cloned()
210    }
211
212    /// Convert the children elements to another object (if there are any).
213    ///
214    /// ```
215    /// # let children = Children::new(Vec::new());
216    /// # use yew::{classes, html, Children};
217    /// # let _ =
218    /// children.map(|children| {
219    ///     html! {
220    ///         <div class={classes!("container")}>
221    ///             {children}
222    ///         </div>
223    ///     }
224    /// })
225    /// # ;
226    /// ```
227    pub fn map<OUT: Default>(&self, closure: impl FnOnce(&Self) -> OUT) -> OUT {
228        if self.is_empty() {
229            Default::default()
230        } else {
231            closure(self)
232        }
233    }
234}
235
236impl<T> Default for ChildrenRenderer<T> {
237    fn default() -> Self {
238        Self {
239            children: Default::default(),
240        }
241    }
242}
243
244impl<T> fmt::Debug for ChildrenRenderer<T> {
245    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
246        f.write_str("ChildrenRenderer<_>")
247    }
248}
249
250impl<T: Clone> IntoIterator for ChildrenRenderer<T> {
251    type IntoIter = std::vec::IntoIter<Self::Item>;
252    type Item = T;
253
254    fn into_iter(self) -> Self::IntoIter {
255        if let Some(children) = self.children {
256            let children = RcExt::unwrap_or_clone(children);
257            children.into_iter()
258        } else {
259            Vec::new().into_iter()
260        }
261    }
262}
263
264impl From<ChildrenRenderer<Html>> for Html {
265    fn from(mut val: ChildrenRenderer<Html>) -> Self {
266        if let Some(children) = val.children.as_mut() {
267            if children.len() == 1 {
268                let children = Rc::make_mut(children);
269                if let Some(m) = children.pop() {
270                    return m;
271                }
272            }
273        }
274
275        Html::VList(Rc::new(val.into()))
276    }
277}
278
279impl From<ChildrenRenderer<Html>> for VList {
280    fn from(val: ChildrenRenderer<Html>) -> Self {
281        VList::from(val.children)
282    }
283}
284
285impl<COMP> From<ChildrenRenderer<VChild<COMP>>> for ChildrenRenderer<Html>
286where
287    COMP: BaseComponent,
288{
289    fn from(value: ChildrenRenderer<VChild<COMP>>) -> Self {
290        Self::new(
291            value
292                .into_iter()
293                .map(VComp::from)
294                .map(VNode::from)
295                .collect::<Vec<_>>(),
296        )
297    }
298}
299
300/// A [Properties] type with Children being the only property.
301#[derive(Debug, Properties, PartialEq)]
302pub struct ChildrenProps {
303    /// The Children of a Component.
304    #[prop_or_default]
305    pub children: Html,
306}
307
308#[cfg(test)]
309mod tests {
310    use super::*;
311
312    #[test]
313    fn children_map() {
314        let children = Children::new(vec![]);
315        let res = children.map(|children| Some(children.clone()));
316        assert!(res.is_none());
317        let children = Children::new(vec![Default::default()]);
318        let res = children.map(|children| Some(children.clone()));
319        assert!(res.is_some());
320    }
321}