Knockout js - && in if condition and containerless binding - javascript

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);

Related

knockout "X is not defined" error

I have a knockout model and am getting an error that I do not understand.
Here is the offending section of code:
<!-- ko with: SearchModel -->
...
<img class="search-img" data-bind="visible: searching" src="#Url.Content("~/Static/Hypercube_Large_Light_Transparent.gif")" height="30" />
<img id="searchIcon" class="search-img" data-bind="visible: !searching()" src="#Url.Content("~/Static/search_icon.png")" height="30" />
...
<!-- /ko -->
In SearchModel.js:
...
this.searching = ko.observable(false);
...
I get an error on the SECOND reference to searching. The first one resolves just fine, and there are no other errors. Weird thing, the code actually WORKS, so whenever I change the searching variable to true, the second image goes away and the first one appears, then when I change it back to false, the first goes away and the second appears.
Uncaught ReferenceError: Unable to parse bindings.
Bindings value: visible: !searching()
Message: searching is not defined
I suspect that you need to check for the existence of SearchModel prior to the with binding.
Something like:
<!-- ko if: SearchModel() -->
Or more specific:
<!-- ko if: SearchModel() !== 'undefined' && SearchModel() !== null -->
You could also, depending on your view model hierarchy, create a ko.pureComputed to check the existence of the SearchModel and in the HTML use code like:
<!-- ko if: SomePureComputed -->

Best way to initialize jquery plugin on DOM element in multiple templates

I'm trying to use chosen on some form elements that are in various templates throughout a meteor app. For example:
HTML
<template name="personSettings">
<!-- some code -->
<template name="chosenSelect">
<select class="chosen">
<!-- options -->
</select>
</template>
<!-- some code -->
</template>
JS
Template.chosenSelect.rendered = function() {
$('.chosen').chosen()
}
Note that the JS at this point is irrelevant, because I get the error:
While building the application:
client/views/personSettings.html:27: Expected "template" end tag
... </select>
I thought you could nest templates, so I'm not sure what the problem is. However, if it turns out I cannot nest templates, the question remains:
What is the best way to initiaize a jquery plugin on a DOM element (by class), that exists in multiple templates?
You can indeed nest templates but your syntax is wrong :
HTML
<template name="chosenSelect">
<select class="chosen">
<!-- options -->
</select>
</template>
<template name="personSettings">
<!-- some code -->
{{> chosenSelect}}
<!-- some code -->
</template>
You must always declare templates within a single pair of matching template tags, if you need to call another template from within your template markup, use the template invokation / inclusion syntax {{> myTemplate}}.
JS
Template.chosenSelect.onRendered(function() {
this.$(".chosen").chosen()
});
When it comes to initializng jQuery plugins within template instances, use the onRendered lifecycle event and prefer using scoped jQuery object (this.$).

Knockout containerless 'with' binding not working

This containerless with binding doesn't set the bindingContext as I would expect; it's still set to the containing parent of ladder.
<!-- ko with:ladder -->
<table>
//Context here is the $root object, not $root.ladder
//some foreach binding here
</table>
Add 4 pages
<!-- /ko -->
This containerful method works fine though.
<table class="ladder-table" data-bind="with:ladder">
//the context is correctly set to ladder in this instance
//some foreach binding here
</table>
<br />
Add 4 pages
Anyone know what's up with that? Google didn't give any results.
The problem is with Durandal, not Knockout, as this answer explains:
containerless statements of knockoutjs is not working in hottowel SPA?
In short, Durandal allows only one root element per view.
goodView.html
<div>
<--ko foreach:stuff-->
//stuff
<--/ko-->
</div>
badView.html
<div>
<stuff/>
</div>
<--ko foreach:stuff--> //these elements are stripped out
//stuff
<--/ko-->
Thanks #nemesv

KO cannot find template with ID

I've used Knockout templates before, so I'm not sure why this isn't working for me.
I tried two different styles of ko markup, neither work.
<!-- more nesting levels -->
<div class="cal-day-tps" data-bind="foreach: timePeriods">
<div class="cal-day-tp-cont">
<div data-bind="template: { name: 'tp-ed-templ', data: $data }"></div>
//both of these methods fail
<!-- ko template: { name: 'tp-ed-templ', data: $data } -->
<!-- /ko -->
</div>
</div>
<!-- /more nesting levels -->
<script type="text/html" id="tp-ed-templ">
<!-- bunch of markup -->
</script>
I just get the error "Cannot find template with ID tp-ed-templ".
Probably just a typo, but I haven't been able to find it.
I'm using KO in the context of Durandal, though this shouldn't make a difference.
Tried declaring the template before usage, didn't help.
Someone else ran into the same thing with no solution either
It seems to be an issue with Durandal, not Knockout.
I tried some extremely simple cases in vanilla durandal setups, and it still does the same thing. Even tried putting the script in the same nested location as the binding, no dice.
The short answer: You can't currently use Knockout templates inside of Durandal.
However, as nemesv pointed out, if you put your named template outside of Durandal, ko is able to find them. For example, anywhere outside of the <div id="applicationHost"></div> element.
The other workarounds are to either use Durandal's compose functionality, or just inline the templates as anonymous.
Knockout templates will probably be supported in the near future.
I finally dug these answers up on the Durandal google group,
Mixing knockout templates with durandal compose
knockout can't find templates inside of views
The issue is that the KO template element must exist in the DOM before the Durandal view is bound. This is because the view is bound before it is inserted into the DOM so any contained templates cannot be resolved by ID.
Using a function that returns an observable can be used to later re-trigger a template binding .. it works, but is wonky. (An if binding could be used for similar effect.)
// bind to this in markup:
// <div data-bind="template: {name: $root.templateName, .. }">
vm.templateName = function () {
return vm.TemplateId();
};
// Changing this will trigger an observable in the KO template binding;
// don't ask me why we have to pass in a function to 'name' ..
vm.TemplateId = ko.observable("dummy-template-id-that-exists");
// After the view is attached the correct template element is in the DOM
// so we can trigger the template to (re-)bind and it will find it.
function viewAttached () {
vm.TemplateId("the-real-template-id");
}

AngularJS bind specific data to template

I'm currently making a switch from Knockout to Angular. The main problem I'm having right now is in transferring my original templates to something Angular will recognise.
Specifically, here's a bit of code I'm having trouble transferring:
<!-- ko template: { name: 'SquareTempl', data: Squares[5] } --><!-- /ko -->
In Knockout, this will attach Squares[5] to SquareTempl, so that when the template gets rendered, it does so using the members within Squares[5](or whatever data that gets attached).
I need to repeat the process for Squares[0]~Squares[11]. I can't use ng-repeat though since I won't be iterating through them in numerical order.
Ideally, it would be nice if I could do something along the lines of
<td class="Square" id="five" ng-include src="'SquareTempl.html'" ng-data="Squares[5]">
Any ideas?
Here's a JSFiddle I've written to outline a failed attempt I've tried using ng-model.
http://jsfiddle.net/fZz3W/9/
Two things: First, you can make ng-data be available by implementing it yourself YourApp.directive("ngData", function() {}) Secondly, do you need the HTML to be part of another file? An easy way to accomplish what you're looking for in Angular is with ng-repeat like:
<td ng-repeat="item in Square">
<div>{{item.name}}</div>
</td>
When the Square array is updated an additional post will be made.
Review your modified JSFiddle: http://jsfiddle.net/TdWMF/1/
So this is mostly a hack to achieve what you want, until I can offer you a better solution:
Second update using mixed order ng-repeat: http://jsfiddle.net/TdWMF/3/
Basically:
<div ng-repeat="index in [4, 2, 0, 3, 1]">
square index: {{index}}<br />
square: {{Squares[index]}}
</div>
Pretty ugly, and non-ideal, I know. I'd also recommend performing the order array generate in a function and then doing: ng-repeat="index in displayOrder(Squares)"

Categories