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