Fearing it is a bug, hoping it is just me:
I am using a jquery.tmpl template in a knockout.js data-binding. The template uses the jquery.tmpl notation to bind to properties: ${Name}
. It turns out that when the 'Name' property is null
, the template renders a 4 letter string "null"
into the HTML. This is awkward. (Note: Name is a ko.observable in this case).
If I use the template manually $("#rowTemplate").tmpl(item).appendTo($("tbody"))
and Name
is not an observable then jQuery template renders an empty string. This is much more like the behaviour I'd expect.
Drawing the whole thing one step further, if I do a null-check in the template by using:
{{if Name != null}}
<input value="${Name}" />
{{/if}}
Then plain jQuery template does not render the input box. However, when using the template with knockout.js, the condition Name != null
is false, because Name
is an observable. On the other hand, {{if Name() != null}}
is an invalid jQuery template syntax.
I did not find any way around this issue. Would anyone be able to tell me how to use jQuery templates correctly in conjunction with knockout.js? Or do I have to report a bug to the knockout project?
Example HTML document:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Test Page</title>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"></script>
<script type="text/javascript" src="jquery.tmpl.min.js"></script>
<script type="text/javascript" src="knockout-1.3.0beta.js"></script>
<script type="text/javascript" src="knockout.mapping-latest.js"></script>
</head>
<body>
<table>
<thead><tr><th>Name</th></tr></thead>
<tbody data-bind="template: { name: 'rowTemplate', foreach: items }"> </tbody>
</table>
<script id="rowTemplate" type="text/x-jquery-tmpl">
{{if Name != null}}
<tr><td>${Name}</td></tr>
{{/if}}
</script>
<script type="text/javascript">
var rawItems = [{ "Name": null }, { "Name": "Me"}];
var boundData = {
items: ko.mapping.fromJS(rawItems)
};
$(document).ready(function () {
// Knockout way:
ko.applyBindings(ko.mapping.fromJS({ items: rawItems }));
// Non-knockout way:
//$.each(rawItems, function () {
// $("#rowTemplate").tmpl(this).appendTo($("tbody"));
//});
});
</script>
</body>
</html>
Doing {{if Name() != null}}
or even just {{if Name()}}
is the right syntax in the if
. http://jsfiddle.net/rniemeyer/92ssx/.
Otherwise, you can switch to <td data-bind="text: Name"></td>
which will at least render empty string. One additional alternative is to do ${Name() ? Name : ''}