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

lifecycle.mddocs/

0

# Lifecycle Hooks

1

2

Component lifecycle management hooks for handling mounting, updating, rendering, and cleanup operations.

3

4

## Capabilities

5

6

### onWillStart

7

8

Called before the component starts its initial rendering process.

9

10

```typescript { .api }

11

/**

12

* Hook called before component starts rendering

13

* Supports both synchronous and asynchronous operations

14

* @param callback - Function to execute, can return Promise

15

*/

16

function onWillStart(callback: () => void | Promise<void>): void;

17

```

18

19

**Usage Examples:**

20

21

```typescript

22

import { Component, xml, onWillStart, useState } from "@odoo/owl";

23

24

class DataLoader extends Component {

25

static template = xml`

26

<div>

27

<div t-if="state.loading">Loading...</div>

28

<div t-else="">

29

<h2><t t-esc="state.data.title" /></h2>

30

<p><t t-esc="state.data.description" /></p>

31

</div>

32

</div>

33

`;

34

35

setup() {

36

this.state = useState({

37

loading: true,

38

data: null

39

});

40

41

// Async data loading before component renders

42

onWillStart(async () => {

43

try {

44

const response = await fetch("/api/data");

45

this.state.data = await response.json();

46

} catch (error) {

47

console.error("Failed to load data:", error);

48

this.state.data = { title: "Error", description: "Failed to load" };

49

} finally {

50

this.state.loading = false;

51

}

52

});

53

}

54

}

55

```

56

57

### onMounted

58

59

Called after the component has been mounted to the DOM.

60

61

```typescript { .api }

62

/**

63

* Hook called after component is mounted to DOM

64

* @param callback - Function to execute after mounting

65

*/

66

function onMounted(callback: () => void): void;

67

```

68

69

**Usage Examples:**

70

71

```typescript

72

import { Component, xml, onMounted, useRef } from "@odoo/owl";

73

74

class ChartComponent extends Component {

75

static template = xml`

76

<div>

77

<canvas t-ref="canvas" width="400" height="300"></canvas>

78

</div>

79

`;

80

81

setup() {

82

this.canvasRef = useRef("canvas");

83

84

onMounted(() => {

85

// DOM is ready, we can manipulate elements

86

const canvas = this.canvasRef.el;

87

const ctx = canvas.getContext("2d");

88

89

// Draw chart

90

this.drawChart(ctx);

91

92

// Focus management

93

if (this.props.autoFocus) {

94

canvas.focus();

95

}

96

97

// Third-party library initialization

98

this.chartInstance = new ThirdPartyChart(canvas, this.props.chartData);

99

});

100

}

101

102

drawChart(ctx) {

103

// Chart drawing logic

104

ctx.fillStyle = "blue";

105

ctx.fillRect(0, 0, 100, 50);

106

}

107

}

108

```

109

110

### onWillUpdateProps

111

112

Called before component props are updated.

113

114

```typescript { .api }

115

/**

116

* Hook called before props update

117

* @param callback - Function receiving the next props, can return Promise

118

*/

119

function onWillUpdateProps(callback: (nextProps: any) => void | Promise<void>): void;

120

```

121

122

**Usage Examples:**

123

124

```typescript

125

import { Component, xml, onWillUpdateProps, useState } from "@odoo/owl";

126

127

class UserProfile extends Component {

128

static template = xml`

129

<div>

130

<img t-att-src="state.avatar" alt="User avatar" />

131

<h2><t t-esc="props.user.name" /></h2>

132

<div t-if="state.loading">Loading profile...</div>

133

</div>

134

`;

135

136

setup() {

137

this.state = useState({

138

avatar: null,

139

loading: false

140

});

141

142

// Load avatar when user changes

143

onWillUpdateProps(async (nextProps) => {

144

if (nextProps.user.id !== this.props.user.id) {

145

this.state.loading = true;

146

try {

147

const response = await fetch(`/api/users/${nextProps.user.id}/avatar`);

148

this.state.avatar = await response.text();

149

} finally {

150

this.state.loading = false;

151

}

152

}

153

});

154

}

155

}

156

```

157

158

### onWillRender

159

160

Called before each rendering cycle.

161

162

```typescript { .api }

163

/**

164

* Hook called before each render

165

* @param callback - Function to execute before rendering

166

*/

167

function onWillRender(callback: () => void): void;

168

```

169

170

**Usage Examples:**

171

172

```typescript

173

import { Component, xml, onWillRender, useState } from "@odoo/owl";

174

175

class AnimatedComponent extends Component {

176

static template = xml`

177

<div t-att-class="state.cssClass">

178

<p>Render count: <t t-esc="state.renderCount" /></p>

179

</div>

180

`;

181

182

setup() {

183

this.state = useState({

184

renderCount: 0,

185

cssClass: ""

186

});

187

188

onWillRender(() => {

189

// Increment render counter

190

this.state.renderCount++;

191

192

// Prepare animations or computations

193

this.state.cssClass = this.state.renderCount % 2 === 0 ? "even-render" : "odd-render";

194

195

console.log("About to render, count:", this.state.renderCount);

196

});

197

}

198

}

199

```

200

201

### onRendered

202

203

Called after each rendering cycle completes.

204

205

```typescript { .api }

206

/**

207

* Hook called after each render completes

208

* @param callback - Function to execute after rendering

209

*/

210

function onRendered(callback: () => void): void;

211

```

212

213

**Usage Examples:**

214

215

```typescript

216

import { Component, xml, onRendered, useRef, useState } from "@odoo/owl";

217

218

class ScrollableList extends Component {

219

static template = xml`

220

<div t-ref="container" class="scrollable-list">

221

<div t-foreach="state.items" t-as="item" t-key="item.id">

222

<t t-esc="item.text" />

223

</div>

224

</div>

225

`;

226

227

setup() {

228

this.containerRef = useRef("container");

229

this.state = useState({

230

items: [],

231

shouldScrollToBottom: false

232

});

233

234

onRendered(() => {

235

// Scroll to bottom after new items are added

236

if (this.state.shouldScrollToBottom && this.containerRef.el) {

237

this.containerRef.el.scrollTop = this.containerRef.el.scrollHeight;

238

this.state.shouldScrollToBottom = false;

239

}

240

241

// Update third-party library after DOM changes

242

if (this.chartInstance) {

243

this.chartInstance.update();

244

}

245

});

246

}

247

248

addItem(text) {

249

this.state.items.push({ id: Date.now(), text });

250

this.state.shouldScrollToBottom = true;

251

}

252

}

253

```

254

255

### onWillPatch

256

257

Called before DOM patching occurs.

258

259

```typescript { .api }

260

/**

261

* Hook called before DOM patch

262

* @param callback - Function to execute before patching

263

*/

264

function onWillPatch(callback: () => void): void;

265

```

266

267

**Usage Examples:**

268

269

```typescript

270

import { Component, xml, onWillPatch, useRef } from "@odoo/owl";

271

272

class VideoPlayer extends Component {

273

static template = xml`

274

<video t-ref="video" t-att-src="props.src" controls="true" />

275

`;

276

277

setup() {

278

this.videoRef = useRef("video");

279

280

onWillPatch(() => {

281

// Save current playback state before DOM changes

282

const video = this.videoRef.el;

283

if (video) {

284

this.savedTime = video.currentTime;

285

this.wasPaused = video.paused;

286

}

287

});

288

}

289

}

290

```

291

292

### onPatched

293

294

Called after DOM patching completes.

295

296

```typescript { .api }

297

/**

298

* Hook called after DOM patch completes

299

* @param callback - Function to execute after patching

300

*/

301

function onPatched(callback: () => void): void;

302

```

303

304

**Usage Examples:**

305

306

```typescript

307

import { Component, xml, onPatched, onWillPatch, useRef } from "@odoo/owl";

308

309

class VideoPlayer extends Component {

310

static template = xml`

311

<video t-ref="video" t-att-src="props.src" controls="true" />

312

`;

313

314

setup() {

315

this.videoRef = useRef("video");

316

317

onWillPatch(() => {

318

// Save state before patch

319

const video = this.videoRef.el;

320

if (video) {

321

this.savedTime = video.currentTime;

322

this.wasPaused = video.paused;

323

}

324

});

325

326

onPatched(() => {

327

// Restore state after patch

328

const video = this.videoRef.el;

329

if (video && this.savedTime !== undefined) {

330

video.currentTime = this.savedTime;

331

if (!this.wasPaused) {

332

video.play();

333

}

334

}

335

});

336

}

337

}

338

```

339

340

### onWillUnmount

341

342

Called before the component is unmounted from the DOM.

343

344

```typescript { .api }

345

/**

346

* Hook called before component unmounts from DOM

347

* @param callback - Function to execute before unmounting

348

*/

349

function onWillUnmount(callback: () => void): void;

350

```

351

352

**Usage Examples:**

353

354

```typescript

355

import { Component, xml, onWillUnmount, onMounted } from "@odoo/owl";

356

357

class TimerComponent extends Component {

358

static template = xml`

359

<div>Timer: <t t-esc="state.seconds" />s</div>

360

`;

361

362

setup() {

363

this.state = useState({ seconds: 0 });

364

365

onMounted(() => {

366

// Start timer

367

this.interval = setInterval(() => {

368

this.state.seconds++;

369

}, 1000);

370

});

371

372

onWillUnmount(() => {

373

// Cleanup timer

374

if (this.interval) {

375

clearInterval(this.interval);

376

}

377

378

// Save state to localStorage

379

localStorage.setItem("timerState", JSON.stringify({

380

seconds: this.state.seconds,

381

timestamp: Date.now()

382

}));

383

});

384

}

385

}

386

```

387

388

### onWillDestroy

389

390

Called before the component is completely destroyed.

391

392

```typescript { .api }

393

/**

394

* Hook called before component is destroyed

395

* @param callback - Function to execute before destruction

396

*/

397

function onWillDestroy(callback: () => void): void;

398

```

399

400

**Usage Examples:**

401

402

```typescript

403

import { Component, xml, onWillDestroy, onMounted } from "@odoo/owl";

404

405

class WebSocketComponent extends Component {

406

static template = xml`

407

<div>

408

<p>Status: <t t-esc="state.status" /></p>

409

<p>Messages: <t t-esc="state.messages.length" /></p>

410

</div>

411

`;

412

413

setup() {

414

this.state = useState({

415

status: "disconnected",

416

messages: []

417

});

418

419

onMounted(() => {

420

// Initialize WebSocket connection

421

this.ws = new WebSocket("ws://localhost:8080");

422

423

this.ws.onopen = () => {

424

this.state.status = "connected";

425

};

426

427

this.ws.onmessage = (event) => {

428

this.state.messages.push(JSON.parse(event.data));

429

};

430

431

this.ws.onclose = () => {

432

this.state.status = "disconnected";

433

};

434

});

435

436

onWillDestroy(() => {

437

// Close WebSocket connection

438

if (this.ws && this.ws.readyState === WebSocket.OPEN) {

439

this.ws.close();

440

}

441

442

// Unsubscribe from global services

443

GlobalEventBus.off("data-update", this.handleDataUpdate);

444

445

// Clean up any pending async operations

446

if (this.pendingRequest) {

447

this.pendingRequest.abort();

448

}

449

});

450

}

451

}

452

```

453

454

### onError

455

456

Called when an error occurs in the component or its children.

457

458

```typescript { .api }

459

/**

460

* Hook called when component or child errors occur

461

* @param callback - Function to handle errors

462

*/

463

function onError(callback: (error: Error) => void): void;

464

```

465

466

**Usage Examples:**

467

468

```typescript

469

import { Component, xml, onError, useState } from "@odoo/owl";

470

471

class ErrorBoundary extends Component {

472

static template = xml`

473

<div>

474

<div t-if="state.hasError" class="error-message">

475

<h3>Something went wrong!</h3>

476

<p><t t-esc="state.errorMessage" /></p>

477

<button t-on-click="resetError">Try Again</button>

478

</div>

479

<div t-else="">

480

<t t-slot="default" />

481

</div>

482

</div>

483

`;

484

485

setup() {

486

this.state = useState({

487

hasError: false,

488

errorMessage: ""

489

});

490

491

onError((error) => {

492

console.error("Component error caught:", error);

493

494

this.state.hasError = true;

495

this.state.errorMessage = error.message;

496

497

// Report error to monitoring service

498

this.reportError(error);

499

});

500

}

501

502

resetError() {

503

this.state.hasError = false;

504

this.state.errorMessage = "";

505

}

506

507

reportError(error) {

508

// Send to error tracking service

509

fetch("/api/errors", {

510

method: "POST",

511

headers: { "Content-Type": "application/json" },

512

body: JSON.stringify({

513

error: error.message,

514

stack: error.stack,

515

timestamp: new Date().toISOString()

516

})

517

});

518

}

519

}

520

```