0
# Template System
1
2
XML-based template system with compilation, runtime evaluation, and template management for component rendering.
3
4
## Capabilities
5
6
### xml Template Function
7
8
Creates template strings using tagged template literals for component templates.
9
10
```typescript { .api }
11
/**
12
* Tagged template literal for creating XML templates
13
* @param args - Template string parts and interpolated values
14
* @returns Template string with unique identifier
15
*/
16
function xml(...args: Parameters<typeof String.raw>): string;
17
```
18
19
**Usage Examples:**
20
21
```typescript
22
import { Component, xml, useState } from "@odoo/owl";
23
24
// Basic template
25
class SimpleComponent extends Component {
26
static template = xml`
27
<div class="simple">
28
<h1>Hello World!</h1>
29
</div>
30
`;
31
}
32
33
// Template with dynamic content
34
class DynamicComponent extends Component {
35
static template = xml`
36
<div>
37
<h1><t t-esc="props.title" /></h1>
38
<p t-if="state.showDescription">
39
<t t-esc="props.description" />
40
</p>
41
<button t-on-click="toggleDescription">
42
<t t-esc="state.showDescription ? 'Hide' : 'Show'" />
43
</button>
44
</div>
45
`;
46
47
setup() {
48
this.state = useState({ showDescription: false });
49
}
50
51
toggleDescription() {
52
this.state.showDescription = !this.state.showDescription;
53
}
54
}
55
56
// Template with loops and conditions
57
class ListComponent extends Component {
58
static template = xml`
59
<div class="list-container">
60
<h2>Items (<t t-esc="props.items.length" />)</h2>
61
<ul t-if="props.items.length > 0">
62
<li t-foreach="props.items" t-as="item" t-key="item.id"
63
t-att-class="{ 'completed': item.completed }">
64
<span t-esc="item.title" />
65
<button t-on-click="() => this.toggleItem(item.id)">
66
<t t-esc="item.completed ? 'Undo' : 'Complete'" />
67
</button>
68
</li>
69
</ul>
70
<p t-else="">No items found.</p>
71
</div>
72
`;
73
74
toggleItem(id) {
75
const item = this.props.items.find(i => i.id === id);
76
if (item) {
77
item.completed = !item.completed;
78
}
79
}
80
}
81
82
// Template with sub-components
83
class ParentComponent extends Component {
84
static template = xml`
85
<div class="parent">
86
<Header title="props.title" />
87
<main>
88
<TodoList items="state.todos" />
89
</main>
90
<Footer />
91
</div>
92
`;
93
94
static components = { Header, TodoList, Footer };
95
96
setup() {
97
this.state = useState({
98
todos: [
99
{ id: 1, title: "Learn OWL", completed: false }
100
]
101
});
102
}
103
}
104
```
105
106
107
### Template Configuration
108
109
Configuration options for template sets and compilation.
110
111
```typescript { .api }
112
/**
113
* Template set configuration options
114
*/
115
interface TemplateSetConfig {
116
/** Enable development mode with additional checks */
117
dev?: boolean;
118
/** List of attributes that should be translated */
119
translatableAttributes?: string[];
120
/** Function to translate strings */
121
translateFn?: (s: string, translationCtx: string) => string;
122
/** Initial templates as string, Document, or record */
123
templates?: string | Document | Record<string, string>;
124
/** Custom template getter function */
125
getTemplate?: (s: string) => Element | Function | string | void;
126
/** Custom directive definitions */
127
customDirectives?: customDirectives;
128
/** Global values object for template access */
129
globalValues?: object;
130
}
131
```
132
133
**Usage Examples:**
134
135
```typescript
136
import { TemplateSet, App, Component } from "@odoo/owl";
137
138
// Internationalization setup
139
const i18nTemplateSet = new TemplateSet({
140
dev: process.env.NODE_ENV === "development",
141
translateFn: (text, context) => {
142
return i18n.t(text, { context });
143
},
144
translatableAttributes: [
145
"title", "placeholder", "aria-label", "aria-description",
146
"alt", "label", "data-tooltip"
147
],
148
globalValues: {
149
formatDate: (date) => new Intl.DateTimeFormat().format(date),
150
formatCurrency: (amount, currency) => new Intl.NumberFormat('en', {
151
style: 'currency',
152
currency
153
}).format(amount)
154
}
155
});
156
157
// Template set with custom directives
158
const customTemplateSet = new TemplateSet({
159
customDirectives: {
160
"tooltip": {
161
compile(node, directive, context) {
162
const expr = directive.value;
163
return {
164
pre: `node.setAttribute('data-tooltip', ${expr});`,
165
post: `initTooltip(node);`
166
};
167
}
168
},
169
"lazy-load": {
170
compile(node, directive, context) {
171
return {
172
pre: `
173
if ('IntersectionObserver' in window) {
174
const observer = new IntersectionObserver((entries) => {
175
entries.forEach(entry => {
176
if (entry.isIntersecting) {
177
entry.target.src = entry.target.dataset.src;
178
observer.unobserve(entry.target);
179
}
180
});
181
});
182
observer.observe(node);
183
}
184
`
185
};
186
}
187
}
188
}
189
});
190
191
// App with custom template configuration
192
const app = new App(RootComponent, {
193
dev: true,
194
templates: `
195
<templates>
196
<t t-name="shared-button">
197
<button t-att-class="className" t-on-click="onClick">
198
<t t-esc="label" />
199
</button>
200
</t>
201
</templates>
202
`,
203
translateFn: (text) => translations[text] || text,
204
globalValues: {
205
utils: {
206
capitalize: (str) => str.charAt(0).toUpperCase() + str.slice(1),
207
truncate: (str, length) => str.length > length ? str.slice(0, length) + "..." : str
208
}
209
}
210
});
211
```
212
213
## Template Directives
214
215
### Core Directives
216
217
```typescript
218
// Content directives
219
t-esc="expression" // Escaped text content
220
t-raw="expression" // Raw HTML content
221
t-out="expression" // Output expression
222
223
// Conditional rendering
224
t-if="condition" // Conditional rendering
225
t-elif="condition" // Else-if condition
226
t-else="" // Else condition
227
228
// Loops
229
t-foreach="items" t-as="item" t-key="item.id" // Loop over items
230
231
// Attributes
232
t-att="object" // Set multiple attributes from object
233
t-att-class="expression" // Dynamic class attribute
234
t-att-style="expression" // Dynamic style attribute
235
t-att-[attr]="expression" // Dynamic single attribute
236
237
// Event handling
238
t-on-[event]="handler" // Event listener
239
t-on-[event].prevent="handler" // With preventDefault
240
t-on-[event].stop="handler" // With stopPropagation
241
242
// Components
243
t-component="ComponentClass" // Render component
244
t-props="propsObject" // Pass props to component
245
246
// References and slots
247
t-ref="refName" // Element reference
248
t-slot="slotName" // Named slot
249
t-set="variable" t-value="expression" // Set variable
250
251
// Sub-templates
252
t-call="templateName" // Call named template
253
```
254
255
### Template Best Practices
256
257
- Use `t-key` for list items to optimize rendering
258
- Prefer `t-esc` over `t-raw` for security
259
- Use `t-att-class` with objects for conditional classes
260
- Cache complex expressions in computed properties
261
- Use sub-templates for reusable template parts