0
# Template System
1
2
Flexible templating system supporting multiple template engines, template sources, and rendering options for dynamic content generation. The template system provides the foundation for data-driven UI rendering in Knockout.js.
3
4
## Capabilities
5
6
### Template Rendering
7
8
Core template rendering functions for generating HTML content with data binding.
9
10
```javascript { .api }
11
/**
12
* Render template with data
13
* @param template - Template string, node, or function
14
* @param dataOrBindingContext - Data object or binding context (optional)
15
* @param options - Template rendering options (optional)
16
* @param targetNodeOrNodeArray - Target node(s) for rendering (optional)
17
* @param renderMode - How to render into target (optional)
18
* @returns Computed observable for DOM updates or string for string templates
19
*/
20
function renderTemplate(template: string | Node | (() => string | Node)): string;
21
function renderTemplate<T>(
22
template: string | Node | (() => string | Node),
23
dataOrBindingContext: T | BindingContext<T> | null | undefined,
24
options?: TemplateOptions<T> | null | undefined
25
): string;
26
function renderTemplate<T>(
27
template: string | Node | (() => string | Node),
28
dataOrBindingContext: T | BindingContext<T> | null | undefined,
29
options: TemplateOptions<T> | null | undefined,
30
targetNodeOrNodeArray: Node | Node[],
31
renderMode?: "replaceChildren" | "replaceNode" | "ignoreTargetNode"
32
): Computed<void>;
33
```
34
35
**Usage Examples:**
36
37
```javascript
38
import ko from "knockout";
39
40
// Render template as string
41
const htmlString = ko.renderTemplate("<span>Hello, {{name}}!</span>");
42
43
// Render template with data
44
const data = { name: "World", count: 5 };
45
const html = ko.renderTemplate(
46
"<p>Hello, <span data-bind='text: name'></span>! Count: <span data-bind='text: count'></span></p>",
47
data
48
);
49
50
// Render template into DOM element
51
const targetElement = document.getElementById("content");
52
ko.renderTemplate(
53
"<div data-bind='text: message'></div>",
54
{ message: ko.observable("Dynamic content") },
55
{ afterRender: (elements, data) => console.log("Rendered:", elements) },
56
targetElement,
57
"replaceChildren"
58
);
59
60
// Render template from DOM element
61
const templateElement = document.getElementById("my-template");
62
ko.renderTemplate(templateElement, data, null, targetElement);
63
```
64
65
### Template Options
66
67
Configuration options for template rendering behavior and callbacks.
68
69
```typescript { .api }
70
interface TemplateOptions<T = any> {
71
/** Custom template engine to use */
72
templateEngine?: TemplateEngine;
73
/** Callback after template is rendered */
74
afterRender?: (elements: Node[], dataItem: T) => void;
75
}
76
77
interface TemplateForeachOptions<T = any> extends TemplateOptions<T[]> {
78
/** Alias name for loop variable */
79
as?: string;
80
/** Include items marked as destroyed */
81
includeDestroyed?: boolean;
82
/** Callbacks for array change animations */
83
beforeMove?: (elements: Node[], index: number, dataItem: T) => void;
84
beforeRemove?: (elements: Node[], index: number, dataItem: T) => void;
85
afterAdd?: (elements: Node[], index: number, dataItem: T) => void;
86
afterMove?: (elements: Node[], index: number, dataItem: T) => void;
87
afterRemove?: (elements: Node[], index: number, dataItem: T) => void;
88
}
89
```
90
91
**Usage Examples:**
92
93
```javascript
94
import ko from "knockout";
95
96
// Template with afterRender callback
97
ko.renderTemplate(
98
"<div data-bind='text: message'></div>",
99
{ message: "Hello" },
100
{
101
afterRender: function(elements, data) {
102
// Initialize widgets, apply styling, etc.
103
elements.forEach(el => {
104
if (el.nodeType === 1) { // Element node
105
el.classList.add("rendered");
106
}
107
});
108
}
109
},
110
document.getElementById("target")
111
);
112
113
// Foreach template with animation callbacks
114
const items = ko.observableArray([
115
{ name: "Item 1" },
116
{ name: "Item 2" }
117
]);
118
119
ko.renderTemplate(
120
"<div data-bind='foreach: { data: items, afterAdd: slideIn, beforeRemove: slideOut }'>" +
121
" <div data-bind='text: name'></div>" +
122
"</div>",
123
{
124
items: items,
125
slideIn: function(elements) {
126
elements.forEach(el => {
127
el.style.opacity = "0";
128
el.animate([
129
{ opacity: 0, transform: "translateY(-20px)" },
130
{ opacity: 1, transform: "translateY(0)" }
131
], { duration: 300 });
132
});
133
},
134
slideOut: function(elements) {
135
elements.forEach(el => {
136
el.animate([
137
{ opacity: 1, transform: "translateY(0)" },
138
{ opacity: 0, transform: "translateY(-20px)" }
139
], { duration: 300 });
140
});
141
}
142
}
143
);
144
```
145
146
### Template Engine Management
147
148
Functions for managing and configuring template engines.
149
150
```javascript { .api }
151
/**
152
* Set the global template engine
153
* @param templateEngine - Template engine instance or undefined for default
154
*/
155
function setTemplateEngine(templateEngine: TemplateEngine | undefined): void;
156
```
157
158
**Usage Examples:**
159
160
```javascript
161
import ko from "knockout";
162
163
// Set custom template engine
164
const customEngine = new MyCustomTemplateEngine();
165
ko.setTemplateEngine(customEngine);
166
167
// Reset to default engine
168
ko.setTemplateEngine(undefined);
169
```
170
171
### Template Engine Interface
172
173
Base template engine class for creating custom template processors.
174
175
```typescript { .api }
176
abstract class TemplateEngine {
177
/** Whether template rewriting is allowed */
178
allowTemplateRewriting: boolean;
179
180
/**
181
* Render template source with binding context
182
* @param templateSource - Template source object
183
* @param bindingContext - Current binding context
184
* @param options - Template options
185
* @param templateDocument - Document context (optional)
186
* @returns Array of rendered DOM nodes
187
*/
188
abstract renderTemplateSource(
189
templateSource: TemplateSource,
190
bindingContext: BindingContext<any>,
191
options: TemplateOptions<any>,
192
templateDocument?: Document
193
): Node[];
194
195
/**
196
* Create JavaScript evaluation block
197
* @param script - JavaScript code
198
* @returns Wrapped script block
199
*/
200
createJavaScriptEvaluatorBlock(script: string): string;
201
202
/**
203
* Create template source from template
204
* @param template - Template string or node
205
* @param templateDocument - Document context (optional)
206
* @returns Template source object
207
*/
208
makeTemplateSource(template: string | Node, templateDocument?: Document): TemplateSource;
209
210
/**
211
* Render template with binding context
212
* @param template - Template string or node
213
* @param bindingContext - Binding context
214
* @param options - Template options
215
* @param templateDocument - Document context (optional)
216
* @returns Array of rendered nodes
217
*/
218
renderTemplate(
219
template: string | Node,
220
bindingContext: BindingContext<any>,
221
options: TemplateOptions<any>,
222
templateDocument?: Document
223
): Node[];
224
225
/**
226
* Check if template has been rewritten
227
* @param template - Template to check
228
* @param templateDocument - Document context (optional)
229
* @returns True if template was rewritten
230
*/
231
isTemplateRewritten(template: string | Node, templateDocument?: Document): boolean;
232
233
/**
234
* Rewrite template using callback
235
* @param template - Template to rewrite
236
* @param rewriterCallback - Rewrite function
237
* @param templateDocument - Document context (optional)
238
*/
239
rewriteTemplate(
240
template: string | Node,
241
rewriterCallback: (template: string) => string,
242
templateDocument?: Document
243
): void;
244
}
245
```
246
247
### Built-in Template Engines
248
249
Knockout.js includes several built-in template engine implementations.
250
251
```javascript { .api }
252
/**
253
* Native template engine (default)
254
* Uses browser's native template capabilities
255
*/
256
class NativeTemplateEngine extends TemplateEngine {
257
renderTemplateSource(
258
templateSource: TemplateSource,
259
bindingContext: BindingContext<any>,
260
options: TemplateOptions<any>,
261
templateDocument?: Document
262
): Node[];
263
}
264
265
/**
266
* jQuery templates engine
267
* Integrates with jQuery templates plugin
268
*/
269
class JQueryTmplTemplateEngine extends TemplateEngine {
270
renderTemplateSource(
271
templateSource: TemplateSource,
272
bindingContext: BindingContext<any>,
273
options: TemplateOptions<any>,
274
templateDocument?: Document
275
): Node[];
276
277
createJavaScriptEvaluatorBlock(script: string): string;
278
279
/**
280
* Add named template to jQuery templates
281
* @param templateName - Template name
282
* @param templateMarkup - Template HTML
283
*/
284
addTemplate(templateName: string, templateMarkup: string): void;
285
}
286
```
287
288
**Usage Examples:**
289
290
```javascript
291
import ko from "knockout";
292
293
// Use jQuery templates engine
294
const jqueryEngine = new ko.jqueryTmplTemplateEngine();
295
ko.setTemplateEngine(jqueryEngine);
296
297
// Add named template
298
jqueryEngine.addTemplate("userTemplate",
299
"<div><h3>${name}</h3><p>${email}</p></div>"
300
);
301
302
// Use native engine (default)
303
const nativeEngine = new ko.nativeTemplateEngine();
304
ko.setTemplateEngine(nativeEngine);
305
```
306
307
### Template Sources
308
309
Template source objects that provide template content and metadata.
310
311
```typescript { .api }
312
interface TemplateSource {
313
/** Get or set template text content */
314
text(): string;
315
text(value: string): void;
316
317
/** Get or set template data */
318
data(key: string): any;
319
data<T>(key: string): T;
320
data<T>(key: string, value: T): void;
321
322
/** Get or set template nodes (optional) */
323
nodes?(): Node;
324
nodes?(value: Node): void;
325
}
326
```
327
328
```javascript { .api }
329
const templateSources: {
330
/**
331
* DOM element template source
332
* Uses content of DOM element as template
333
*/
334
domElement: {
335
new(element: Node): TemplateSource;
336
337
text(): string;
338
text(value: string): void;
339
data(key: string): any;
340
data<T>(key: string, value: T): void;
341
nodes(): Node;
342
nodes(value: Node): void;
343
};
344
345
/**
346
* Anonymous template source
347
* For templates created dynamically
348
*/
349
anonymousTemplate: {
350
new(element: Node): TemplateSource;
351
};
352
};
353
```
354
355
**Usage Examples:**
356
357
```javascript
358
import ko from "knockout";
359
360
// Create template source from DOM element
361
const templateElement = document.getElementById("my-template");
362
const domSource = new ko.templateSources.domElement(templateElement);
363
364
// Get template content
365
const templateText = domSource.text();
366
console.log("Template:", templateText);
367
368
// Set template data
369
domSource.data("cacheKey", "unique-identifier");
370
domSource.data("compiled", true);
371
372
// Get template data
373
const cacheKey = domSource.data("cacheKey");
374
const isCompiled = domSource.data("compiled");
375
376
// Create anonymous template
377
const anonymousElement = document.createElement("div");
378
anonymousElement.innerHTML = "<span data-bind='text: message'></span>";
379
const anonymousSource = new ko.templateSources.anonymousTemplate(anonymousElement);
380
```
381
382
### Template Binding Options
383
384
Extended options for template binding in HTML.
385
386
```typescript { .api }
387
interface BindingTemplateOptions extends TemplateOptions {
388
/** Template name or function returning name */
389
name?: string | ((data: any) => string);
390
/** Template nodes to use instead of name */
391
nodes?: Node[];
392
/** Data to pass to template */
393
data?: any;
394
/** Array data for foreach rendering */
395
foreach?: any[];
396
/** Conditional rendering */
397
if?: boolean;
398
ifnot?: boolean;
399
/** Alias for loop variable */
400
as?: string;
401
/** Include destroyed items in foreach */
402
includeDestroyed?: boolean;
403
/** Animation callbacks */
404
beforeMove?: (elements: Node[], index: number, dataItem: any) => void;
405
beforeRemove?: (elements: Node[], index: number, dataItem: any) => void;
406
afterAdd?: (elements: Node[], index: number, dataItem: any) => void;
407
afterMove?: (elements: Node[], index: number, dataItem: any) => void;
408
afterRemove?: (elements: Node[], index: number, dataItem: any) => void;
409
}
410
```
411
412
**Usage in HTML:**
413
414
```html
415
<!-- Named template -->
416
<div data-bind="template: 'user-template'"></div>
417
418
<!-- Template with data -->
419
<div data-bind="template: { name: 'user-template', data: selectedUser }"></div>
420
421
<!-- Foreach template -->
422
<div data-bind="template: { name: 'item-template', foreach: items }"></div>
423
424
<!-- Conditional template -->
425
<div data-bind="template: { name: 'details-template', if: showDetails }"></div>
426
427
<!-- Template with alias -->
428
<div data-bind="template: { name: 'user-template', foreach: users, as: 'user' }">
429
<!-- Inside template: user.name, $index(), $parent, etc. -->
430
</div>
431
432
<!-- Inline template nodes -->
433
<div data-bind="template: { nodes: $element.childNodes, data: currentItem }">
434
<p>This content will be used as template</p>
435
<span data-bind="text: name"></span>
436
</div>
437
438
<!-- Template with animations -->
439
<div data-bind="template: {
440
name: 'animated-item',
441
foreach: items,
442
afterAdd: slideIn,
443
beforeRemove: slideOut
444
}"></div>
445
446
<!-- Named templates in script tags -->
447
<script type="text/html" id="user-template">
448
<div class="user">
449
<h3 data-bind="text: name"></h3>
450
<p data-bind="text: email"></p>
451
<button data-bind="click: $parent.editUser">Edit</button>
452
</div>
453
</script>
454
455
<script type="text/html" id="item-template">
456
<li data-bind="text: title, css: { completed: isDone }"></li>
457
</script>
458
```
459
460
### Custom Template Engine Example
461
462
Example of creating a custom template engine with preprocessing.
463
464
```javascript
465
// Custom template engine with Handlebars-like syntax
466
function HandlebarsTemplateEngine() {
467
this.allowTemplateRewriting = true;
468
}
469
470
HandlebarsTemplateEngine.prototype = Object.create(ko.templateEngine.prototype);
471
472
HandlebarsTemplateEngine.prototype.renderTemplateSource = function(templateSource, bindingContext, options) {
473
// Get template text
474
let template = templateSource.text();
475
476
// Simple Handlebars-like preprocessing
477
template = template.replace(/\{\{(\w+)\}\}/g, (match, prop) => {
478
return `<span data-bind="text: ${prop}"></span>`;
479
});
480
481
// Create temporary element with processed template
482
const tempDiv = document.createElement("div");
483
tempDiv.innerHTML = template;
484
485
// Apply bindings to processed template
486
ko.applyBindingsToDescendants(bindingContext, tempDiv);
487
488
// Return child nodes
489
return Array.from(tempDiv.childNodes);
490
};
491
492
// Register and use custom engine
493
const customEngine = new HandlebarsTemplateEngine();
494
ko.setTemplateEngine(customEngine);
495
496
// Usage with custom syntax
497
ko.renderTemplate(
498
"<div>Hello, {{name}}! You have {{count}} messages.</div>",
499
{ name: "Alice", count: 5 }
500
);
501
```