or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

advanced-features.mdcore-components.mdindex.mdmethod-lifecycle-decorators.mdproperty-decorators.md

method-lifecycle-decorators.mddocs/

0

# Method and Lifecycle Decorators

1

2

Decorators for watchers, event emitters, dependency injection, and lifecycle hooks in Vue components.

3

4

## Capabilities

5

6

### Watch Decorator

7

8

Decorator for reactive data watchers with comprehensive configuration options.

9

10

```typescript { .api }

11

/**

12

* Decorator for reactive data watchers

13

* @param key - The reactive property or path to watch

14

* @param options - Optional watcher configuration

15

* @returns Method decorator

16

*/

17

function Watch(key: string, options?: WatchOptions): MethodDecorator;

18

19

interface WatchOptions {

20

flush?: 'post';

21

deep?: boolean;

22

immediate?: boolean;

23

}

24

25

interface WatchConfig {

26

key: string;

27

handler: WatchCallback;

28

flush?: 'post';

29

deep?: boolean;

30

immediate?: boolean;

31

}

32

```

33

34

**Usage Examples:**

35

36

```typescript

37

import { Component, Setup, Watch } from "vue-facing-decorator";

38

import { ref, reactive } from "vue";

39

40

@Component

41

class WatchComponent {

42

@Setup(() => ref("initial"))

43

message!: string;

44

45

@Setup(() => ref(0))

46

count!: number;

47

48

@Setup(() => reactive({ nested: { value: "deep" } }))

49

deepObject!: { nested: { value: string } };

50

51

// Basic watcher

52

@Watch("message")

53

onMessageChange(newVal: string, oldVal: string) {

54

console.log(`Message changed from "${oldVal}" to "${newVal}"`);

55

}

56

57

// Immediate watcher

58

@Watch("count", { immediate: true })

59

onCountChange(newVal: number, oldVal: number) {

60

console.log(`Count: ${newVal} (was: ${oldVal})`);

61

}

62

63

// Deep watcher for objects

64

@Watch("deepObject", { deep: true })

65

onDeepObjectChange(newVal: any, oldVal: any) {

66

console.log("Deep object changed:", newVal, oldVal);

67

}

68

69

// Post-flush watcher (after DOM updates)

70

@Watch("message", { flush: "post" })

71

onMessageChangePost(newVal: string) {

72

// This runs after DOM updates

73

console.log("DOM updated with new message:", newVal);

74

}

75

76

// Multiple watchers on same property

77

@Watch("count")

78

firstCountWatcher(newVal: number) {

79

console.log("First watcher:", newVal);

80

}

81

82

@Watch("count", { immediate: true })

83

secondCountWatcher(newVal: number) {

84

console.log("Second watcher:", newVal);

85

}

86

}

87

```

88

89

### Emit Decorator

90

91

Decorator for event emission methods with custom event names.

92

93

```typescript { .api }

94

/**

95

* Decorator for event emission methods

96

* @param eventName - Optional custom event name, defaults to method name

97

* @returns Method decorator

98

*/

99

function Emit(eventName?: string): MethodDecorator;

100

101

type EmitConfig = null | string;

102

```

103

104

**Usage Examples:**

105

106

```typescript

107

import { Component, Emit, Setup } from "vue-facing-decorator";

108

import { ref } from "vue";

109

110

@Component

111

class EmitComponent {

112

@Setup(() => ref("Hello"))

113

message!: string;

114

115

// Basic emit - event name matches method name

116

@Emit()

117

clicked() {

118

return { timestamp: Date.now() };

119

}

120

121

// Custom event name

122

@Emit("customEvent")

123

handleAction() {

124

return this.message;

125

}

126

127

// Emit without return value

128

@Emit("simpleEvent")

129

simpleAction() {

130

console.log("Action performed");

131

}

132

133

// Async emit

134

@Emit("asyncEvent")

135

async asyncAction() {

136

await new Promise(resolve => setTimeout(resolve, 1000));

137

return { result: "async complete" };

138

}

139

140

// Emit with parameters

141

@Emit("dataChange")

142

changeData(newData: any) {

143

this.message = newData.message;

144

return newData;

145

}

146

147

// Multiple emits from same method

148

@Emit("start")

149

@Emit("process")

150

complexAction() {

151

return { action: "complex", data: this.message };

152

}

153

}

154

155

// Usage in parent:

156

// <EmitComponent

157

// @clicked="onClicked"

158

// @customEvent="onCustom"

159

// @simpleEvent="onSimple"

160

// @asyncEvent="onAsync"

161

// @dataChange="onDataChange"

162

// />

163

```

164

165

### Provide Decorator

166

167

Decorator for providing reactive data to descendant components.

168

169

```typescript { .api }

170

/**

171

* Decorator for providing reactive data to descendant components

172

* @param key - Optional provide key, defaults to property name

173

* @returns Property decorator

174

*/

175

function Provide(key?: string): PropertyDecorator;

176

177

type ProvideConfig = null | string;

178

```

179

180

**Usage Examples:**

181

182

```typescript

183

import { Component, Provide, Setup } from "vue-facing-decorator";

184

import { ref, reactive } from "vue";

185

186

@Component

187

class ProvideComponent {

188

// Provide with default key (property name)

189

@Provide()

190

@Setup(() => ref("provided value"))

191

sharedData!: string;

192

193

// Provide with custom key

194

@Provide("customKey")

195

@Setup(() => reactive({ theme: "dark", lang: "en" }))

196

appConfig!: { theme: string; lang: string };

197

198

// Provide computed value

199

@Provide("computedValue")

200

@Setup(() => computed(() => `Config: ${this.appConfig.theme}/${this.appConfig.lang}`))

201

configSummary!: string;

202

203

// Provide method

204

@Provide()

205

updateTheme(newTheme: string) {

206

this.appConfig.theme = newTheme;

207

}

208

209

// Provide static value

210

@Provide("staticKey")

211

staticValue = "never changes";

212

}

213

```

214

215

### Inject Decorator

216

217

Decorator for injecting provided data from ancestor components.

218

219

```typescript { .api }

220

/**

221

* Decorator for injecting provided data from ancestor components

222

* @param config - Optional injection configuration

223

* @returns Property decorator

224

*/

225

function Inject(config?: InjectConfig): PropertyDecorator;

226

227

interface InjectConfig {

228

from?: string | symbol | Symbol | InjectionKey<any>;

229

default?: any;

230

}

231

```

232

233

**Usage Examples:**

234

235

```typescript

236

import { Component, Inject } from "vue-facing-decorator";

237

import { InjectionKey } from "vue";

238

239

// Define injection keys for type safety

240

const ThemeKey: InjectionKey<string> = Symbol('theme');

241

const ConfigKey: InjectionKey<{theme: string, lang: string}> = Symbol('config');

242

243

@Component

244

class InjectComponent {

245

// Basic inject - uses property name as key

246

@Inject()

247

sharedData!: string;

248

249

// Inject with custom key

250

@Inject({ from: "customKey" })

251

appConfig!: { theme: string; lang: string };

252

253

// Inject with default value

254

@Inject({ from: "maybeUndefined", default: "default value" })

255

optionalValue!: string;

256

257

// Inject with symbol key for type safety

258

@Inject({ from: ThemeKey })

259

theme!: string;

260

261

@Inject({ from: ConfigKey, default: () => ({ theme: "light", lang: "en" }) })

262

config!: { theme: string; lang: string };

263

264

// Inject method

265

@Inject({ from: "updateTheme" })

266

updateTheme!: (theme: string) => void;

267

268

mounted() {

269

console.log("Injected data:", this.sharedData);

270

console.log("App config:", this.appConfig);

271

272

// Call injected method

273

if (this.updateTheme) {

274

this.updateTheme("light");

275

}

276

}

277

}

278

```

279

280

### Hook Decorator

281

282

Decorator for explicitly marking methods as lifecycle hooks.

283

284

```typescript { .api }

285

/**

286

* Decorator for explicitly marking methods as lifecycle hooks

287

* @returns Method decorator

288

*/

289

function Hook(): MethodDecorator;

290

291

type HookConfig = null;

292

```

293

294

**Usage Examples:**

295

296

```typescript

297

import { Component, Hook, Setup } from "vue-facing-decorator";

298

import { ref } from "vue";

299

300

@Component

301

class HookComponent {

302

@Setup(() => ref("component data"))

303

data!: string;

304

305

// Explicit hook decoration (optional - hooks are auto-detected by name)

306

@Hook()

307

mounted() {

308

console.log("Component mounted");

309

}

310

311

@Hook()

312

beforeUnmount() {

313

console.log("About to unmount");

314

}

315

316

// Regular lifecycle hooks (auto-detected, no decorator needed)

317

created() {

318

console.log("Component created");

319

}

320

321

updated() {

322

console.log("Component updated");

323

}

324

325

// Custom method that happens to have hook name but isn't a hook

326

@Hook()

327

customMounted() {

328

// This will be treated as a regular method, not a lifecycle hook

329

console.log("Custom method with hook-like name");

330

}

331

}

332

```

333

334

## Lifecycle Hook Support

335

336

All Vue 3 lifecycle hooks are automatically supported by name:

337

338

```typescript { .api }

339

// Vue 3 Lifecycle Hooks (all supported automatically)

340

interface LifecycleHooks {

341

beforeCreate?(): void;

342

created?(): void;

343

beforeMount?(): void;

344

mounted?(): void;

345

beforeUpdate?(): void;

346

updated?(): void;

347

activated?(): void;

348

deactivated?(): void;

349

beforeUnmount?(): void;

350

unmounted?(): void;

351

renderTracked?(): void;

352

renderTriggered?(): void;

353

errorCaptured?(): void;

354

serverPrefetch?(): void;

355

render?(): VNode;

356

}

357

358

// Legacy Vue 2 hooks (still supported)

359

interface LegacyHooks {

360

beforeDestroy?(): void;

361

destroyed?(): void;

362

}

363

```

364

365

**Complete Lifecycle Example:**

366

367

```typescript

368

import { Component, Setup, Watch, Emit, Provide, Inject } from "vue-facing-decorator";

369

import { ref } from "vue";

370

371

@Component

372

class CompleteLifecycleComponent {

373

@Setup(() => ref(0))

374

counter!: number;

375

376

@Provide()

377

@Setup(() => ref("provided from parent"))

378

sharedValue!: string;

379

380

@Inject({ from: "parentData", default: "no parent" })

381

parentData!: string;

382

383

// All lifecycle hooks

384

beforeCreate() {

385

console.log("Before create - component instance being created");

386

}

387

388

created() {

389

console.log("Created - component instance created");

390

}

391

392

beforeMount() {

393

console.log("Before mount - about to mount");

394

}

395

396

mounted() {

397

console.log("Mounted - component mounted to DOM");

398

}

399

400

beforeUpdate() {

401

console.log("Before update - reactive data changed, about to update");

402

}

403

404

updated() {

405

console.log("Updated - DOM updated");

406

}

407

408

beforeUnmount() {

409

console.log("Before unmount - about to unmount");

410

}

411

412

unmounted() {

413

console.log("Unmounted - component unmounted");

414

}

415

416

// Watcher

417

@Watch("counter")

418

onCounterChange(newVal: number, oldVal: number) {

419

this.counterChanged(newVal, oldVal);

420

}

421

422

// Emitter

423

@Emit("counterChanged")

424

counterChanged(newVal: number, oldVal: number) {

425

return { newVal, oldVal, timestamp: Date.now() };

426

}

427

428

increment() {

429

this.counter++;

430

}

431

}

432

```