# Feature: Componentes
Los componentes permiten dividir XML grande en funciones pequenas. La idea es
similar a Leptos o JSX, pero el resultado es XML estatico y deterministico, no
un DOM reactivo.
## Que aprenderas aqui
- [ ] Definir props tipadas.
- [ ] Recibir children.
- [ ] Retornar un elemento o un fragment.
- [ ] Componer componentes desde `xml!`.
- [ ] Iterar listas con componentes.
## Archivo
- [ ] `src/component/mod.rs`
## Modelo mental
Un componente es una funcion Rust:
```rust
fn Component(props: Props, children: Children) -> XmlResult<ElementBuilder>
```
No hay runtime especial. No hay lifecycle. No hay reactividad. Solo Rust normal
que retorna builders/fragments.
## Primer componente
```rust
use xdoc::builder::ElementBuilder;
use xdoc::core::XmlResult;
use xdoc::macros::xml;
#[derive(Clone)]
struct HeaderProps {
id: String,
}
#[allow(non_snake_case)]
fn Header(props: HeaderProps) -> XmlResult<ElementBuilder> {
xml! {
<Header>
<ID>{ props.id }</ID>
</Header>
}?
.into_fragment()
.into_single_element()
}
```
Uso:
```rust
let document = xml! {
<Document>
<{Header} id={ "DOC-001".to_owned() }/>
</Document>
}?
.into_document()?;
```
La macro crea `HeaderProps { id: ... }` usando los atributos pasados al
componente.
## Componentes con children
```rust
use xdoc::builder::ElementBuilder;
use xdoc::component::Children;
use xdoc::core::XmlResult;
use xdoc::macros::xml;
#[derive(Clone)]
struct SectionProps {
name: &'static str,
}
#[allow(non_snake_case)]
fn Section(props: SectionProps, children: Children) -> XmlResult<ElementBuilder> {
xml! {
<Section name={ props.name }>
{ children }
</Section>
}?
.into_fragment()
.into_single_element()
}
```
Uso:
```rust
let document = xml! {
<Document>
<{Section} name="summary">
<Title>Summary</Title>
<Text>Reusable children</Text>
</{Section}>
</Document>
}?
.into_document()?;
```
## Componentes que retornan fragments
Un componente tambien puede retornar varios nodos si devuelve `Fragment`.
```rust
use xdoc::builder::{element, fragment};
use xdoc::component::Fragment;
use xdoc::core::XmlResult;
#[derive(Clone)]
struct PairProps {
first: &'static str,
second: &'static str,
}
#[allow(non_snake_case)]
fn Pair(props: PairProps) -> XmlResult<Fragment> {
fragment()
.child(element("First")?.text(props.first)?)?
.child(element("Second")?.text(props.second)?)
}
```
Uso:
```rust
let document = xml! {
<Root>
<{Pair} first="one" second="two"/>
</Root>
}?
.into_document()?;
```
Salida:
```xml
<Root><First>one</First><Second>two</Second></Root>
```
## Listas con componentes
Puedes mapear datos Rust a componentes y luego insertar el vector.
```rust
#[derive(Clone)]
struct ItemProps {
code: &'static str,
name: &'static str,
}
#[allow(non_snake_case)]
fn Item(props: ItemProps) -> XmlResult<ElementBuilder> {
xml! {
<Item code={ props.code }>
<Name>{ props.name }</Name>
</Item>
}?
.into_fragment()
.into_single_element()
}
let items = vec![
ItemProps { code: "A", name: "Alpha" },
ItemProps { code: "B", name: "Beta" },
];
let item_nodes = items
.iter()
.map(|item| xml! { <{Item} code={ item.code } name={ item.name }/> })
.collect::<XmlResult<Vec<_>>>()?;
let document = xml! {
<Catalog>{ item_nodes }</Catalog>
}?
.into_document()?;
```
## Props tipadas
Las props son structs. Esto te da:
- [ ] Errores de compilacion si falta un campo.
- [ ] Tipos Rust reales para numeros, fechas propias, enums, etc.
- [ ] Refactors mas seguros.
- [ ] Separacion clara entre datos y forma XML.
En el MVP, los props que pasan por `xml!` deben ser identificadores simples.
Para valores mas complejos, calcula el valor antes:
```rust
let total_text = format!("{:.2}", total);
let node = xml! { <Amount>{ total_text }</Amount> }?;
```
## Cuando usar componentes
Usa componentes si:
- [ ] Tu XML es grande.
- [ ] Varias secciones se repiten.
- [ ] Quieres encapsular reglas de construccion.
- [ ] Quieres probar piezas pequenas.
- [ ] Quieres mantener props tipadas.
No necesitas componentes si:
- [ ] Tu XML es pequeno y plano.
- [ ] Un solo `xml!` o builder ya se entiende bien.
## Errores comunes
- [ ] Olvidar `#[allow(non_snake_case)]` si quieres nombres estilo componente.
- [ ] Intentar usar props complejos directamente dentro del tag; calcula antes.
- [ ] Retornar un fragment con varios nodos y luego convertirlo a documento.
- [ ] Pensar que `Children` es una closure; aqui es un fragment ya construido.
## API principal
- [ ] `Children`
- [ ] `Fragment`
- [ ] `FragmentNode`
- [ ] `IntoXml`
- [ ] `IntoXmlFragment`
- [ ] `document`
- [ ] `children`
## Siguiente lectura
- [ ] [macros.md](macros.md) para escribir componentes con tags.
- [ ] [builder.md](builder.md) para crear componentes sin macro.
- [ ] [writer.md](writer.md) para serializar.
[Volver al indice](features.md).