or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

app-components.mdblockdom.mdhooks.mdindex.mdlifecycle.mdreactivity.mdtemplates.mdutils-validation.md

templates.mddocs/

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