Why self works inside #[component]?
The macro #[component] just rewrites function into a struct which implements mount method. This method accepts self which is instantly destructured into variables in accordance to the original function arguments.
The below example shows the same component wrapped with component macro and in a simplified expanded version:
use vertigo::{component, dom}; #[component] fn MyComponent(age: i32) { dom! { <p>"Age: " {self.age}</p> } } // The same component implemented manually: struct MyComponentExpanded { pub age: i32, } impl MyComponentExpanded { pub fn into_component(self) -> Self { self } pub fn mount(self) -> vertigo::DomNode { let age = self.age; { dom! { <p>"Age: " {self.age}</p> } } } } dom! { <div> <MyComponentExpanded age=21 /> <MyComponent age=24 /> </div> }
Why .into_component method? This is to allow components with dynamic/optional attributes. The dom! macro turns <MyComponent ... /> into MyComponent { ... }.into_component(), then pushes attributes from groups (like inner:id="...") into it, and finally calls .mount(). This is how the macro really expands a component with an attribute group:
use vertigo::{component, dom, AttrGroup}; #[component] fn MyComponent(age: i32, inner: AttrGroup) { dom! { <p {..inner}>"Age: " {self.age}</p> } } // The same component expanded: struct MyComponentExpanded { pub age: i32, } struct __MyComponentExpandedComponent { pub age: i32, pub inner: AttrGroup, } impl MyComponentExpanded { pub fn into_component(self) -> __MyComponentExpandedComponent { __MyComponentExpandedComponent { age: self.age, inner: Default::default(), } } // Shorthand for `.into_component().mount()` pub fn mount(self) -> vertigo::DomNode { self.into_component().mount() } } impl __MyComponentExpandedComponent { // Used for `inner:key=value` attributes pub fn group_inner_push(mut self, key: String, value: vertigo::AttrGroupValue) -> Self { self.inner.insert(key, value); self } // Used for `inner={..group}` attribute pub fn group_inner_replace(mut self, group: AttrGroup) -> Self { self.inner = group; self } pub fn mount(self) -> vertigo::DomNode { let age = self.age; let inner = self.inner; dom! { <p {..inner}>"Age: " {self.age}</p> } } } dom! { <div> <MyComponentExpanded age=21 inner:id="expanded" /> <MyComponent age=24 inner:id="generated" /> </div> }
Children
A component can also take children. The dom! macro collects everything placed between the opening and closing tag into a Vec<DomNode> and passes it as the children argument:
use vertigo::{component, dom, DomNode}; #[component] fn Card<'a>(title: &'a str, children: Vec<DomNode>) { dom! { <section> <h3>{title}</h3> {..children} </section> } } dom! { <Card title="Hello"> <p>"Everything between the tags"</p> <p>"lands in the children vector."</p> </Card> }