KO cannot find template with ID - javascript

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

Related

How to use a template with textboxes loaded with data passed from a calling template in ember.js

I'm trying to make a addstudent template which can be rendered with data if clicked on editstudent option from another template.
I don't want to make different templates for addStudentDetails and EditstudentDetails. how to create a single template for this both functions.
can anyone help me with this?
I see a couple ways of doing this.
Handlebar template, using conditionals.
Then in your controller you can control whether you want to edit or add.
The ember guide has a good example of this for controlling state.
Or you can use Routes and Resources. Possibly a Parent Resource called Student and then routes called edit and add. But you said you didn't want to use different templates so I'm assuming you'll probably want to use #1.
//inside your controller
App.StudentController = Ember.ObjectController.extend({
editStudent: true // or false
});
<!-- handlebars template possibly called studentTemplate -->
{{#if editStudent}}
<!-- code for editStudent -->
{{else}}
<!-- code for addStudent -->
{{/if}}
Hope that answers your question.

Knockout.js - How to toggle between views

Alright, newbie Knockout question here.
In this example: http://learn.knockoutjs.com/WebmailExampleStandalone.html#Inbox
How does the mail detail view replace the folder list view?
That is, what feature causes the divs to be toggled? In inspecting the dom, I see that what happens is the div's are actually rendered empty when not displayed.
Can someone enlighten me? I know this is rather basic, but its the last piece I need to click into place for my understanding.
Just to be 100% clear: when you click on a row in the folder list, what causes the folder view to be emptied and the mail detail to display? Is it the with binding? (That doesn't seem right.)
You are on the right track with the with binding: in this example the views are changed using the with binding relaying on this feature of the binding:
The with binding will dynamically add or remove descendant elements depending on whether the associated value is null/undefined or not
So in the viewmodel code you will see something like this:
this.get('#:folder', function () {
self.chosenFolderId(this.params.folder);
self.chosenMailData(null);
$.get("/mail", { folder: this.params.folder }, self.chosenFolderData);
});
this.get('#:folder/:mailId', function () {
self.chosenFolderId(this.params.folder);
self.chosenFolderData(null);
$.get("/mail", { mailId: this.params.mailId }, self.chosenMailData);
});
So the functions which are "chaining" the view nulls out one of the properties while filling in the other which toggles the views defined as:
<!-- Chosen mail -->
<div class="viewMail" data-bind="with: chosenMailData">
...
<div/>
<!-- Mails grid -->
<table class="mails" data-bind="with: chosenFolderData">
</table>
This is not the nicest solution but don't forget that Knockout is a Databind/MVVM library and not a full blown SPA framework so it does not have concepts for layouting and higher level view composition.
However this could be made nicer with using the template binding:
<div id="mainView" data-bind="{template: {name: templateName, data: activeView}}">
</div>
And turning the views into templates:
<script type="text/html" id="ChosenMail">
<div class="viewMail">
...
<div/>
</script>
<script type="text/html" id="MailsGrid">
<table class="mails">
...
</table>
</script>
And in the routing only set the activeView property and lookup the corresponding template name for it:
this.get('#:folder', function () {
$.get("/mail", { folder: this.params.folder }, function(data) {
self.activeView(data);
self.templateName('ChosenMail');
});;
});
this.get('#:folder/:mailId', function () {
$.get("/mail", { mailId: this.params.mailId }, function(data) {
self.activeView(data);
self.templateName('MailsGrid');
});
});
But because this is quite much manual and error prone work I would use something like Durandal.js which is a real SPA framework and it was designed for this kind scenarios.
That is just a Demo on a light weight SPA scenario, with binding is just a inline template binding. Not very useful for a dynamic SPA. Like Nemesv suggests use the template binding.
The problem with the template binding is that its very verbose to use, I have addressed this in my Binding convention library (One of many features)
Instead of doing
<div id="mainView" data-bind="{template: {name: templateName, data: activeView}}">
</div>
You do
<div id="mainView" data-name="activeView">
</div>
My library will do the rest, check out the wiki on templates here
https://github.com/AndersMalmgren/Knockout.BindingConventions/wiki/Template-convention
And a little fiddle
http://jsfiddle.net/xJL7u/11/

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

Knockout template stays in loading in some cases

I used a template in my application like:
<section class="view-list" data-bind="template: { name: viewTemplate, foreach: locaFacts }">
but in some cases, mostly on the first load the output html is lots of Loading... instead of real templates.
It seems that knockout shows Loading... until the view model gets ready. As I cached the results, the second call will show the templates correctly.
Why the first call to template shows Loading... instead of template itself?
EDIT:
I use knockout external templates and infuser.
I was able to correct this by setting ajax option async to false by modifying the template binding like so:
<div class="pagination" data-bind="template: { name: 'pager', data: pager, ajax: { async: false } }">
I changed my template engine and used Durandal's compose feature.
Now everything is OK.

Knockout js - && in if condition and containerless binding

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

Categories