I'm working on some custom bindings, and in one I'd like to be able to show a table from some arrays of strings.
fiddle
I simplified it down to this custom binding:
ko.bindingHandlers.table = {
init: function tableBinding(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
element.innerHTML = tableTemplate;
var innerBindingContext = bindingContext.createChildContext(valueAccessor());
ko.applyBindingsToDescendants(innerBindingContext, element);
return {
controlsDescendantBindings: true
};
}
};
This is the contents of template:
<!-- if: head && head.length -->
<thead>
<tr data-bind="foreach: head">
<th data-bind="text: $rawData">not working th</th>
</tr>
</thead>
<!-- /ko -->
<tbody data-bind="foreach: rows">
<tr data-bind="foreach: $data">
<td data-bind="text: $data">not working td</td>
</tr>
</tbody>
And some example data.
ko.applyBindings({
table: {
head: ["Name", "Position"],
rows: [
["John", "Janitor"],
["Mike", "IT"],
["Paul", "HR"],
["Rick", "Coffee Fetcher"]
]
}
});
I'm using Knockout 3.0, however anything that'd work on Knockout 2.x would also work here. If you look at the fiddle, the <thead> part is displaying properly, but the bindings for the body aren't. It works fine if I inline the template, and use a with binding, as in, with: table.
I have to confess, at the moment I am not following all you are doing here, but I can tell enough that your example will work if your if statement uses ko if: instead of just if:.
http://jsfiddle.net/AhLzS/1/
So instead of this:
<!-- if: head && head.length -->
go with this:
<!-- ko if: head && head.length -->
The containerless binding syntax requires <!-- ko ... --> ... <!-- /ko --> as the virtual container. Thus if an html comment syntax just has <!-- if ... -->, knockout does not do anything special.
From the knockout documentation for the "if" binding:
http://knockoutjs.com/documentation/if-binding.html
The <!-- ko --> and <!-- /ko --> comments act as start/end markers,
defining a “virtual element” that contains the markup inside. Knockout
understands this virtual element syntax and binds as if you had a real
container element.
Related
I need to add a datepicker to a textbox input, whose visibility is controlled by a knockoutjs binding.
<!-- ko if: hasWorked -->
<div class="form-group">
<input id="StartDate" name="StartDate" type="text" />
</div>
<!-- /ko -->
My guess is:
<!-- ko if: hasWorked, afterRender:initDatepicker -->
But it never worked.
What is the correct way to show a textbox and then run a function to process it?
afterRender is only available to foreach and template.
Luckily, we can do containerless template.
So the solution:
<!-- ko template: {if: hasWorked,afterRender:initDatepicker} -->
...
<!-- /ko -->
References:
Containerless template The solution provided by Maksym
Kozlenko. Not the selected answer.
The use of afterRender in template, see the knockout official documentation on template, (scroll down to Note 4).
This question already has an answer here:
Knockout.js containerless "foreach" not working with <table>
(1 answer)
Closed 9 years ago.
I am new to knockout.js and trying to figure why the next code yields exception, while seeming to be valid knockout binding.
<table>
<!-- ko foreach: [] -->
<tr></tr>
<!-- /ko -->
</table>
jsFiddle: http://jsfiddle.net/Esfk5/
Putting it into a <tbody> seems to resolve the error. Fiddle here, but to summarize the HTML is:
<table>
<tbody>
<!-- ko foreach: [] -->
<tr></tr>
<!-- /ko -->
</tbody>
</table>
Take a look at the following code, with a focus on the embedded script:
<tr>
<!-- ko foreach: { data: foos, as: 'f' } -->
<td>
<!-- ko if: f.someCondition() -->
<input id='picker' />
<script type="text/javascript">
$("#picker").kendoDatePicker({
value: new Date(),
change: f.changeFunction }); <!---- note this line -->
</script>
<!-- /ko -->
</td>
<!-- /ko -->
</tr>
See f.changeFunction? I'm getting a JavaScript error that f is not defined. How do I access the current binding context so I can attach the Kendo change handler to my current foo? I've tried using a few of the binding context variables but they aren't accessible outside of the binding expressions. If I can't access the binding context in the embedded script, is there a workaround?
Is there a reason you aren't using the kendo-knockout bindings?
http://rniemeyer.github.io/knockout-kendo/web/DatePicker.html
I am displaying a list of items and if the items are not available I have to display a default message. Now, I have to check whether the object has been created and then check the object has the list in it.
So now, I am doing the below and it works it creates unnecessary dom elements. But, when I do the same with the containerless binding it doesn't seem to work and also is there an && syntax for if in KO
<span data-bind="if: object">
<span data-bind="if: !object().property">
<p> The list is not available </p>
</span>
</span> // Works
<!-- ko if: object -->
<!-- ko if: !object().property -->
<p> The list is not available </p>
<!-- /ko -->
<!-- /ko --> // Doesn't work
Thanks
As mentioned by CodeThug, using the solutions you provided would display the message until ko.applyBindings have finished. A more verbose solution, but that would avoid the problem without relying on CSS, is to use dynamic templates as shown in the following jsfiddle:
http://jsfiddle.net/sAkb4/1/
This will create the valid markup inside the virtual element only when the ko.applyBindings is done.
<!-- ko template: { name: dinamycList } -->
<!-- /ko -->
<script type="text/html" id="empty-template">
... list is NOT available markup ...
</script>
<script type="text/html" id="list-template">
... list is available markup ...
</script>
Being dinamycList a function that returns the name of the template according to the verifications you want for a valid list.
EDIT:
Reading thru your last comment made me think if the behaviour that you want is to display the "not avaiable template" only after calculating the list and the property is false, if thats the case, the following fiddle will fix the last one to provide the right condition.
http://jsfiddle.net/sAkb4/3/
The "if" condition in template will handle the moment after knockout is ready, but before the list is. If the condition gets too messy, i would advise to put it inside a ko.computed for a clear markup.
<!-- ko template: { name: dinamycList, if: object() !== undefined && object().property !== undefined } -->
<!-- /ko -->
As you have seen, the element exists in the DOM, and it won't be removed until the ko.applyBindings call is finished. Thus the momentary display of this message.
I'd try hiding the entire section of the DOM and show a loading indicator instead. When ko/ajax is done, the loading indicator can then be removed and the markup you care about displayed.
Alternately, you could see if your page is taking a while to load and try to improve the load time of the page. The chrome profiling tools can help with this.
THis is very easy with my Convention over configuration lib
http://jsfiddle.net/VVb4P/
Just this and the lib will do the templatating for you
this.items.subscribe(function(items) {
if(items.length === 0) {
this.items.push(new MyApp.EmptyViewModel());
}
}, this);
I'm trying to use knockout to do a pretty basic binding but am having trouble accessing the $item variable from jquery.tmpl. I keep getting "$item is not defined" when I apply the bindings.
I've done this before so I know it can be done but I can't figure out why it is not working in this case. What's interesting is if I remove templateOptions:{parentItem: $item}, then everything works as expected.
I've included the following files
<script type="text/javascript" src="Extension/resources/jquery.1.6.1.js"></script>
<script type="text/javascript" src="Extension/resources/jquery.tmpl.js"></script>
<script type="text/javascript" src="Extension/resources/knockout-1.2.1.js"></script>
My template and bindings look like this
<script type="text/html" id="itemTemplate">
<span data-bind="text:title"></span>
</script>
<div class="filterResults">
<span data-bind="text:message"></span>
# of items: <span data-bind="text:contentItems().length"></span>
<table cellspacing="0">
<tr data-bind="template: { name: 'itemTemplate', foreach: contentItems,
templateOptions:{parentItem: $item}
}"> </tr>
</table>
</div>
And I do the binding using:
//viewModel contains a contentItems observableArray
ko.applyBindings(viewModel, $('.filterResults')[0]);
Ok, looks like I can answer this one myself. The problem seems to be that the part where I did the data-bind and was trying to access the jquery.tmpl variable $item is not actually using jquery.tmpl
<tr data-bind="template: { name: 'itemTemplate', foreach: contentItems,
templateOptions:{parentItem: $item}
}"> </tr>
That is not inside a template so there is no $item variable to access. $item is associated with jquery.tmpl, not knockoutjs. The way I got around the issue was to just put my view model in there instead.
<tr data-bind="template: { name: 'itemTemplate', foreach: contentItems,
templateOptions:{parentItem: viewModel}
}"> </tr>
And in the actual templates, (itemTemplate in this example), I can access the $item variable.