or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

binding.mdcomponents.mdindex.mdobservables.mdperformance.mdtemplates.mdutils.md
tile.json

components.mddocs/

0

# Component System

1

2

Reusable UI component system with view model and template composition, registration, and loading capabilities for modular application architecture. Components provide encapsulation and reusability by combining templates and view models into self-contained widgets.

3

4

## Capabilities

5

6

### Component Registration

7

8

Functions for registering, unregistering, and managing component definitions.

9

10

```javascript { .api }

11

/**

12

* Register a component with a name and configuration

13

* @param name - Component name (must be unique)

14

* @param config - Component configuration object

15

*/

16

function register(name: string, config: ComponentConfig): void;

17

18

/**

19

* Unregister a component and clear its cached definition

20

* @param name - Component name to unregister

21

*/

22

function unregister(name: string): void;

23

24

/**

25

* Check if a component is registered

26

* @param name - Component name to check

27

* @returns True if component is registered

28

*/

29

function isRegistered(name: string): boolean;

30

31

/**

32

* Clear cached definition for a component (forces reload)

33

* @param name - Component name

34

*/

35

function clearCachedDefinition(name: string): void;

36

```

37

38

**Usage Examples:**

39

40

```javascript

41

import ko from "knockout";

42

43

// Register a simple component

44

ko.components.register("hello-world", {

45

template: "<h1>Hello, <span data-bind='text: name'></span>!</h1>",

46

viewModel: function(params) {

47

this.name = params.name || "World";

48

}

49

});

50

51

// Register component with external template

52

ko.components.register("user-profile", {

53

template: { element: "user-profile-template" },

54

viewModel: UserProfileViewModel

55

});

56

57

// Check if component exists

58

if (ko.components.isRegistered("hello-world")) {

59

console.log("Component is available");

60

}

61

62

// Unregister component

63

ko.components.unregister("hello-world");

64

```

65

66

### Component Configuration

67

68

Configuration options for defining component templates and view models.

69

70

```typescript { .api }

71

interface ComponentConfig {

72

/** Template configuration */

73

template: TemplateConfig;

74

/** View model configuration (optional) */

75

viewModel?: ViewModelConfig;

76

/** Whether to load synchronously */

77

synchronous?: boolean;

78

/** AMD require path for dynamic loading */

79

require?: string;

80

}

81

82

type TemplateConfig =

83

| string // Inline HTML string

84

| Node[] // Array of DOM nodes

85

| DocumentFragment // Document fragment

86

| TemplateElement // Element reference

87

| RequireConfig; // AMD module

88

89

interface TemplateElement {

90

element: string | Node; // Element ID or DOM node

91

}

92

93

type ViewModelConfig =

94

| ViewModelConstructor // Constructor function

95

| ViewModelFactory // Factory with createViewModel method

96

| ViewModelStatic // Static instance

97

| RequireConfig; // AMD module

98

99

interface ViewModelConstructor {

100

new(params?: any): ViewModel;

101

}

102

103

interface ViewModelFactory {

104

createViewModel: CreateViewModel;

105

}

106

107

interface ViewModelStatic {

108

instance: any;

109

}

110

111

interface RequireConfig {

112

require: string; // AMD module path

113

}

114

115

type CreateViewModel = (params: any, componentInfo: ComponentInfo) => ViewModel;

116

117

interface ViewModel {

118

dispose?: () => void;

119

koDescendantsComplete?: (node: Node) => void;

120

}

121

```

122

123

**Usage Examples:**

124

125

```javascript

126

import ko from "knockout";

127

128

// String template with constructor view model

129

ko.components.register("simple-counter", {

130

template: `

131

<div>

132

<p>Count: <span data-bind="text: count"></span></p>

133

<button data-bind="click: increment">+</button>

134

<button data-bind="click: decrement">-</button>

135

</div>

136

`,

137

viewModel: function(params) {

138

this.count = ko.observable(params.initialCount || 0);

139

this.increment = () => this.count(this.count() + 1);

140

this.decrement = () => this.count(this.count() - 1);

141

}

142

});

143

144

// Element template with factory view model

145

ko.components.register("user-editor", {

146

template: { element: "user-editor-template" },

147

viewModel: {

148

createViewModel: function(params, componentInfo) {

149

return new UserEditorViewModel(params.user, componentInfo.element);

150

}

151

}

152

});

153

154

// AMD module loading

155

ko.components.register("external-widget", {

156

template: { require: "text!templates/widget.html" },

157

viewModel: { require: "viewmodels/widget" }

158

});

159

160

// Static instance view model

161

ko.components.register("singleton-service", {

162

template: "<div>Service Status: <span data-bind='text: status'></span></div>",

163

viewModel: {

164

instance: globalServiceInstance

165

}

166

});

167

```

168

169

### Component Loading

170

171

Functions for retrieving and loading component definitions.

172

173

```javascript { .api }

174

/**

175

* Get component definition asynchronously

176

* @param name - Component name

177

* @param callback - Callback receiving component definition

178

* @returns Request identifier

179

*/

180

function get(name: string, callback: (definition: Component, config: ComponentConfig) => void): string;

181

```

182

183

```typescript { .api }

184

interface Component {

185

/** Compiled template nodes */

186

template: Node[];

187

/** View model factory function (optional) */

188

createViewModel?: CreateViewModel;

189

}

190

191

interface ComponentInfo {

192

/** Component's root DOM element */

193

element: Node;

194

/** Original template nodes before component replaced them */

195

templateNodes: Node[];

196

}

197

```

198

199

**Usage Examples:**

200

201

```javascript

202

import ko from "knockout";

203

204

// Get component definition

205

ko.components.get("user-profile", function(definition, config) {

206

console.log("Component loaded:", definition);

207

console.log("Template nodes:", definition.template);

208

209

if (definition.createViewModel) {

210

const viewModel = definition.createViewModel({

211

userId: 123

212

}, {

213

element: document.getElementById("target"),

214

templateNodes: []

215

});

216

}

217

});

218

```

219

220

### Component Loaders

221

222

Extensible loader system for customizing component loading behavior.

223

224

```javascript { .api }

225

/**

226

* Array of component loaders (processed in order)

227

*/

228

const loaders: Loader[];

229

230

/**

231

* Default component loader implementation

232

*/

233

const defaultLoader: DefaultLoader;

234

```

235

236

```typescript { .api }

237

interface Loader {

238

/** Get component configuration */

239

getConfig?(name: string, callback: (config: ComponentConfig | null) => void): void;

240

241

/** Load complete component */

242

loadComponent?(name: string, config: ComponentConfig, callback: (component: Component | null) => void): void;

243

244

/** Load template only */

245

loadTemplate?(name: string, templateConfig: TemplateConfig, callback: (template: Node[] | null) => void): void;

246

247

/** Load view model only */

248

loadViewModel?(name: string, viewModelConfig: ViewModelConfig, callback: (createViewModel: CreateViewModel | null) => void): void;

249

}

250

251

interface DefaultLoader extends Loader {

252

getConfig(name: string, callback: (config: ComponentConfig | null) => void): void;

253

loadComponent(name: string, config: ComponentConfig, callback: (component: Component) => void): void;

254

loadTemplate(name: string, templateConfig: TemplateConfig, callback: (template: Node[]) => void): void;

255

loadViewModel(name: string, viewModelConfig: ViewModelConfig, callback: (createViewModel: CreateViewModel) => void): void;

256

}

257

```

258

259

**Usage Examples:**

260

261

```javascript

262

import ko from "knockout";

263

264

// Custom loader for database-backed components

265

const databaseLoader = {

266

getConfig: function(name, callback) {

267

// Load component config from database

268

fetch(`/api/components/${name}`)

269

.then(response => response.json())

270

.then(config => callback(config))

271

.catch(() => callback(null));

272

}

273

};

274

275

// Add custom loader (processed before default loader)

276

ko.components.loaders.unshift(databaseLoader);

277

278

// Custom loader for CSS-in-JS templates

279

const cssInJsLoader = {

280

loadTemplate: function(name, templateConfig, callback) {

281

if (templateConfig.cssInJs) {

282

// Custom loading logic for CSS-in-JS templates

283

loadCssInJsTemplate(templateConfig.cssInJs)

284

.then(nodes => callback(nodes))

285

.catch(() => callback(null));

286

} else {

287

callback(null); // Let other loaders handle

288

}

289

}

290

};

291

292

ko.components.loaders.unshift(cssInJsLoader);

293

```

294

295

### Component Binding

296

297

Using components in templates via the component binding.

298

299

**Usage in HTML:**

300

301

```html

302

<!-- Basic component usage -->

303

<div data-bind="component: 'hello-world'"></div>

304

305

<!-- Component with parameters -->

306

<div data-bind="component: { name: 'user-profile', params: { userId: currentUserId } }"></div>

307

308

<!-- Dynamic component name -->

309

<div data-bind="component: { name: selectedComponentName, params: componentParams }"></div>

310

311

<!-- Component with observable parameters -->

312

<div data-bind="component: {

313

name: 'data-grid',

314

params: {

315

data: gridData,

316

pageSize: pageSize,

317

onRowClick: handleRowClick

318

}

319

}"></div>

320

```

321

322

### Custom Elements

323

324

Using components as custom HTML elements.

325

326

**Usage in HTML:**

327

328

```html

329

<!-- Component as custom element -->

330

<hello-world params="name: userName"></hello-world>

331

332

<!-- Complex parameters -->

333

<user-profile params="

334

user: selectedUser,

335

editable: isEditable,

336

onSave: saveUser,

337

onCancel: cancelEdit

338

"></user-profile>

339

340

<!-- Observable parameters -->

341

<data-table params="

342

items: tableData,

343

pageSize: itemsPerPage,

344

sortColumn: currentSortColumn,

345

sortDirection: sortDirection

346

"></data-table>

347

```

348

349

### Component Lifecycle

350

351

Managing component lifecycle with disposal and completion events.

352

353

**Usage Examples:**

354

355

```javascript

356

import ko from "knockout";

357

358

// View model with lifecycle methods

359

function MyComponentViewModel(params, componentInfo) {

360

const self = this;

361

362

// Initialize component

363

this.data = ko.observable(params.initialData);

364

this.isLoading = ko.observable(false);

365

366

// Called when component descendants are complete

367

this.koDescendantsComplete = function(node) {

368

console.log("Component descendants complete", node);

369

// Initialize plugins, setup event handlers, etc.

370

};

371

372

// Called when component is disposed

373

this.dispose = function() {

374

console.log("Component disposed");

375

// Cleanup subscriptions, timers, event handlers, etc.

376

if (self.subscription) {

377

self.subscription.dispose();

378

}

379

};

380

381

// Setup subscriptions

382

this.subscription = this.data.subscribe(function(newValue) {

383

// Handle data changes

384

});

385

}

386

387

ko.components.register("lifecycle-component", {

388

template: "<div>Component content</div>",

389

viewModel: MyComponentViewModel

390

});

391

```

392

393

### Component Communication

394

395

Patterns for communication between components and their parents.

396

397

**Usage Examples:**

398

399

```javascript

400

import ko from "knockout";

401

402

// Parent-to-child communication via parameters

403

function ParentViewModel() {

404

this.childData = ko.observable("Hello Child");

405

this.childConfig = ko.observable({ theme: "dark" });

406

}

407

408

// Child-to-parent communication via callbacks

409

function ChildViewModel(params) {

410

this.data = params.data;

411

this.config = params.config;

412

413

this.handleClick = function() {

414

// Notify parent of events

415

if (params.onChildClick) {

416

params.onChildClick("button clicked", this);

417

}

418

};

419

420

this.updateParent = function() {

421

// Update parent data

422

if (params.onDataUpdate) {

423

params.onDataUpdate({ newValue: "Updated from child" });

424

}

425

};

426

}

427

428

ko.components.register("parent-component", {

429

template: `

430

<div>

431

<child-component params="

432

data: childData,

433

config: childConfig,

434

onChildClick: handleChildClick,

435

onDataUpdate: handleDataUpdate

436

"></child-component>

437

</div>

438

`,

439

viewModel: function() {

440

const self = this;

441

this.childData = ko.observable("Parent data");

442

this.childConfig = ko.observable({ setting: "value" });

443

444

this.handleChildClick = function(message, childViewModel) {

445

console.log("Child clicked:", message);

446

};

447

448

this.handleDataUpdate = function(updateData) {

449

console.log("Child updated:", updateData);

450

};

451

}

452

});

453

```

454

455

### Component Name Detection

456

457

Utility function for detecting component names from DOM nodes.

458

459

```javascript { .api }

460

/**

461

* Get component name for a DOM node (if it represents a component)

462

* @param node - DOM node to check

463

* @returns Component name or null

464

*/

465

function getComponentNameForNode(node: Node): string | null;

466

```

467

468

**Usage Examples:**

469

470

```javascript

471

import ko from "knockout";

472

473

// Check if element is a component

474

const element = document.getElementById("my-element");

475

const componentName = ko.components.getComponentNameForNode(element);

476

477

if (componentName) {

478

console.log(`Element is component: ${componentName}`);

479

} else {

480

console.log("Element is not a component");

481

}

482

```