tachys/view/
template.rs

1use super::{
2    add_attr::AddAnyAttr, Mountable, Position, PositionState, Render,
3    RenderHtml, ToTemplate,
4};
5use crate::{
6    html::attribute::{any_attribute::AnyAttribute, Attribute},
7    hydration::Cursor,
8    renderer::Rndr,
9};
10
11/// A view wrapper that uses a `<template>` node to optimize DOM node creation.
12///
13/// Rather than creating all of the DOM nodes each time it is built, this template will create a
14/// single `<template>` node once, then use `.cloneNode(true)` to clone that entire tree, and
15/// hydrate it to add event listeners and interactivity for this instance.
16pub struct ViewTemplate<V> {
17    view: V,
18}
19
20impl<V> ViewTemplate<V>
21where
22    V: Render + ToTemplate + 'static,
23{
24    /// Creates a new view template.
25    pub fn new(view: V) -> Self {
26        Self { view }
27    }
28
29    fn to_template() -> crate::renderer::types::TemplateElement {
30        Rndr::get_template::<V>()
31    }
32}
33
34impl<V> Render for ViewTemplate<V>
35where
36    V: Render + RenderHtml + ToTemplate + 'static,
37    V::State: Mountable,
38{
39    type State = V::State;
40
41    // TODO try_build/try_rebuild()
42
43    fn build(self) -> Self::State {
44        let tpl = Self::to_template();
45        let contents = Rndr::clone_template(&tpl);
46        self.view
47            .hydrate::<false>(&Cursor::new(contents), &Default::default())
48    }
49
50    fn rebuild(self, state: &mut Self::State) {
51        self.view.rebuild(state)
52    }
53}
54
55impl<V> AddAnyAttr for ViewTemplate<V>
56where
57    V: RenderHtml + ToTemplate + 'static,
58    V::State: Mountable,
59{
60    type Output<SomeNewAttr: Attribute> = ViewTemplate<V>;
61
62    fn add_any_attr<NewAttr: Attribute>(
63        self,
64        _attr: NewAttr,
65    ) -> Self::Output<NewAttr> {
66        panic!("AddAnyAttr not supported on ViewTemplate");
67    }
68}
69
70impl<V> RenderHtml for ViewTemplate<V>
71where
72    V: RenderHtml + ToTemplate + 'static,
73    V::State: Mountable,
74{
75    type AsyncOutput = V::AsyncOutput;
76    type Owned = V::Owned;
77
78    const MIN_LENGTH: usize = V::MIN_LENGTH;
79
80    fn to_html_with_buf(
81        self,
82        buf: &mut String,
83        position: &mut Position,
84        escape: bool,
85        mark_branches: bool,
86        extra_attrs: Vec<AnyAttribute>,
87    ) {
88        self.view.to_html_with_buf(
89            buf,
90            position,
91            escape,
92            mark_branches,
93            extra_attrs,
94        )
95    }
96
97    fn hydrate<const FROM_SERVER: bool>(
98        self,
99        cursor: &Cursor,
100        position: &PositionState,
101    ) -> Self::State {
102        self.view.hydrate::<FROM_SERVER>(cursor, position)
103    }
104
105    fn dry_resolve(&mut self) {
106        self.view.dry_resolve();
107    }
108
109    async fn resolve(self) -> Self::AsyncOutput {
110        self.view.resolve().await
111    }
112
113    fn into_owned(self) -> Self::Owned {
114        self.view.into_owned()
115    }
116}
117
118impl<V> ToTemplate for ViewTemplate<V>
119where
120    V: RenderHtml + ToTemplate + 'static,
121    V::State: Mountable,
122{
123    const TEMPLATE: &'static str = V::TEMPLATE;
124
125    fn to_template(
126        buf: &mut String,
127        class: &mut String,
128        style: &mut String,
129        inner_html: &mut String,
130        position: &mut Position,
131    ) {
132        V::to_template(buf, class, style, inner_html, position);
133    }
134}