Using an external Template in KnockoutJS - templates

is it possible to use an external Template in KnockoutJS like this?
<script type="text/html" id="a_template" src="templates/a_template.html">
</script>
I've tried this solution but didn't get it working.

You can use jquery to dynamically load html into a script element, and then execute knockout based on that.
<script type="text/html" id="template_holder"></script>
<script type="text/javascript">
$('#template_holder').load('templates/a_template.html', function() {
alert('Load was performed.');
//knockout binding goes here
});</script>
Your knockout binding must be done in the callback function though, otherwise there's a chance that you'll be trying to bind before the page has loaded
UPDATE Here's an example I've coded on jsfiddle to demonstrate dynamic loading: http://jsfiddle.net/soniiic/2HxPp/

You could also look at:
https://github.com/ifandelse/Knockout.js-External-Template-Engine

You can also use this Template bootstrapper for KO
Bootstrapper
https://github.com/AndersMalmgren/Knockout.Bootstrap
MVC WebAPI Demo
https://github.com/AndersMalmgren/Knockout.Bootstrap.Demo
It uses a Convention over configuration approuch using this lib
https://github.com/AndersMalmgren/Knockout.BindingConventions
Meaning it will automatically understand that MyViewModel should be matched to MyView
Its also prepared to work nicely in a SPA
Disclaimer: I'm the author behind the 3 libs mentioned above

Here's a little function building off of soniiic's answer:
function loadExternalKnockoutTemplates(callback) {
var sel = 'script[src][type="text/html"]:not([loaded])';
$toload = $(sel);
function oncomplete() {
this.attr('loaded', true);
var $not_loaded = $(sel);
if(!$not_loaded.length) {
callback();
}
}
_.each($toload, function(elem) {
var $elem = $(elem);
$elem.load($elem.attr('src'), _.bind(oncomplete, $elem));
});
}
This'll automatically load all knockout templates on your document, provided their src is set and their type is "text/html". Pass in a callback to be notified when all templates loaded. No idea what happens if any of them fails.
Example usage:
<head>
<script type="text/html" src="kot/template1.html" id="template1"></script>
</head>
<body>
<script>
$(function() {
loadExternalKnockoutTemplates(function() {
// Put your ready code here, like
ko.applyBindings();
});
});
function loadExternalKnockoutTemplates(callback) {
var sel = 'script[src][type="text/html"]:not([loaded])';
$toload = $(sel);
function oncomplete() {
this.attr('loaded', true);
var $not_loaded = $(sel);
if(!$not_loaded.length) {
callback();
}
}
_.each($toload, function(elem) {
var $elem = $(elem);
$elem.load($elem.attr('src'), _.bind(oncomplete, $elem));
});
}
</script>
</body>

Related

JSRender: Render another template from converter or custom tag

I am getting HTML content from a backend service request that might include something (non-JSRender) like this:
<div>{{comp:someComponentId}}</div>
Or we can receive it as:
<div>{{comp someComponentId}}</div>
I want to leverage this syntax to use a converter or a tag in JSRender to effectively convert this to:
<div>{{include tmpl="someComponentId"/}}</div>
Whether the tag/converter does the rendering, or if it can iteratively create the include to have JSRender evaluate it. How do I achieve this?
Does this do what you want?
<script id="myTmpl" type="text/x-jsrender">
{{comp someid/}}
</script>
<script id="id1" type="text/x-jsrender">
what {{:foo}}
</script>
<div id="page"></div>
<script>
$.views.tags("comp", {
render: function(id) {
return $("#"+id).render(this.tagCtx.view.data);
}
});
var myTmpl = $.templates("#myTmpl"),
data = { someid: "id1", foo: "FOO" },
html = myTmpl(data);
$("#page").html(html);
</script>

How to deal with Kendo MVC event binding when ES6 Modules loaded asyncronous by SystemJs

As SystemJs loads modules async you can not bind events into the html file directly. Even when you declare the controls after System.import all functions are undefined at this time.
For example the following is not working:
<script type="text/javascript">
System.import('myModule'); //myModule contains a function called onClick.
</script>
#(Html.Kendo().Button()
.Name("RazorButton")
.Content("RazorButton")
.HtmlAttributes(new { type = "button" })
.Events(ev => ev.Click("onClick")))
<button id="html5Button">Html5 Button</button>
<script type="text/javascript">
$("#html5Button").kendoButton({
click: onClick
});
</script>
In this example onClick is allways undefined.
This is not really a problem with the html5 way as I can init the button into the module. With the MVC wrapper however it is the common way to init the button in here.
Is there a way to use the Kendo MVC wrapper in combination with SystemJs module laoding with no drawbacks?
I did not really found a solution with absolutly no drawback.
However, this solution works for me:
Ceate a methods bindEvents in your Module.
myModule.ts
export class MyModule {
bindEvents = () => {
$("#RazorButton").data("kendoButton").bind("click", this.buttonClick);
}
buttonClick = (e) => {
//do something...
}
}
Create a factory for your Module.
myModuleFactory.ts:
import { MyModule} from "./myModule";
export function Create(): MyModule{
return new MyModule();
}
Import the Factory into the View. Call the Create Methods from the factory and get MyModule as Return value. Then call bindEvents from MyModule.
view.cshtml:
<script type="text/javascript">
System.import('myModuleFactory').then(function (e) {
var vm = e.Create();
vm.bindEvents();
}).catch(console.error.bind(console));
</script>
The drawback in here is, that you need to know the name of the kendo event.

Best method to use external templates with knockout

I have built a html page with some internal templates. See url jsfiddle:
http://jsfiddle.net/hoven002/jQTDH/
What is the best method to make the templates external and how?
Regards,
Kenneth
The best method, in my opinion, is to use this plugin: https://github.com/ifandelse/Knockout.js-External-Template-Engine.
It enables a new template engine that will pull templates from external files. It has some configuration options as well to determine where the templates are pulled from.
I've written a function that loads templates, without any dependency but jQuery. You must mark each <script> tags you want to be dynamically loaded with the attribute data-template-src and put there the path of your HTML file. Following is the code and an example.
Warning: since it uses AJAX to load templates, it needs an HTTP server (it won't work locally with file:// protocol)
/main.js
// Loads all the templates defined with the tag data-template-src.
// Returns a promise that fulfills when all the loads are done.
function loadTemplates() {
var templateLoads = [];
$('[data-template-src]').each(function () {
var templateElement = $(this);
var templateUrl = templateElement.attr('data-template-src');
// Load the template into the element and push the promise to do that into the templateLoads array
templateLoads.push(
$.get(templateUrl)
.done(function (data) {
templateElement.html(data);
})
);
});
return $.when.apply($, templateLoads);
}
// When all templates are loaded, apply bindings
loadTemplates().done(function () {
ko.applyBindings({}, document.getElementById("htmlDoc"));
});
/index.html
<html id="htmlDoc">
<body>
<div data-bind="template: { name: 'exampleTemplate' }"></div>
<script type="text/html" id="exampleTemplate" data-template-src="/assets/exampleTemplate.html"></script>
<script src="/jquery.js"></script>
<script src="/knockout.js"></script>
<script src="/main.js"></script>
</body>
</html>
/assets/exampleTemplate.html
<h1>Hello world</h1>
<div data-bind="template: { name: 'exampleSubTemplate' }"></div>
/assets/exampleSubTemplate.html
<h2>How do you do?</h2>

Is it possible to load handlebar template with script tag? Or define handlebar templates programmatically in Ember.js

Simply enough I do not want to define all my handlebar templates in my html file
I tried this
<script type="text/x-handlebars" data-template-name="nav-bar" src="template.handlebar"></script>
But this did not work. Can I not define templates my template programmatically or even just load handlebar files so that I can reuse and also I feel it makes things a bit more maintainable.
I tried just loading them with ajax and appending them to the head, this works fine I can see it there but ember.js doesn't read it after ember has already been loaded and the templates are not defined.
Or define handlebar templates programatically in Ember.js
You can define templates programmatically by using Ember.Handlebars.compile, see http://jsfiddle.net/pangratz666/wxrxT/:
Ember.View.create({
personName: 'Dr. Tobias Fünke',
template: Ember.Handlebars.compile('Hello {{personName}}')
}).append();​
Or you add compiled templates to Ember.TEMPLATES array, see http://jsfiddle.net/pangratz666/58tFP/:
Ember.TEMPLATES['myFunkyTemplate'] = Ember.Handlebars.compile('Hello {{personName}}');
Ember.View.create({
personName: 'Dr. Tobias Fünke',
templateName: 'myFunkyTemplate'
}).append();​
I would recommend to use some tools like Richard Millan stated. Also take a look at interline/ember-skeleton which offers support for compilation of templates.
You can also patch Ember View to load templates on get
Em.View.reopen({
templateForName: function(name, type) {
if (!name) { return; }
var templates = Em.get(this, 'templates'),
template = Em.get(templates, name);
if (!template) {
$.ajax({
url: 'templates/%#.hbs'.fmt(name),
async: false
}).success(function(data) {
template = Ember.Handlebars.compile(data);
});
}
if (!template) {
throw new Em.Error('%# - Unable to find %# "%#".'.fmt(this, type, name));
}
return template;
}
});
UPDATE: Since Ember 1.0.0-pre.3 this solution probabaly no more work (maybe can be migrated to recent Ember)
So since I still wanted separate files for my templates and I didn't want to define them in strings in the javascript I hacked this together last night
It is a synchronous lazy loader, it loads all the templates first, then ember and the rest of my code,
//fake function so that every loads fine will get redefined in application.js
function initializeApp(){}
function loadTemplates(){
var arg = arguments[0],
next = Array.prototype.slice.call(arguments,1);
if(typeof arg != 'string'){
arg()
}else{
var scriptObj = document.createElement('script');
scriptObj.type = 'text/x-handlebars';
$(scriptObj).attr('data-template-name', arg.replace('.handlebars', '').substring(arg.lastIndexOf('/')+1))
$.get(arg, function(data){
scriptObj.text = data;
document.head.appendChild(scriptObj);
if(next.length > 0) loadTemplates.apply(this, next);
});
}
}
function loadScripts() {
var script = arguments[0],
scriptObj = document.createElement('script'),
next = Array.prototype.slice.call(arguments,1);
scriptObj.type = 'text/javascript';
scriptObj.src = script;
scriptObj.onload = scriptObj.onreadystatechange = (next.length > 0) ? function(){loadScripts.apply(this, next)} : function(){$(document).ready(function() {initializeApp()})};
document.head.appendChild(scriptObj);
}
function loadApp(obj){
loadTemplates.apply(this, obj.templates.concat(function(){loadScripts.apply(this,obj.scripts)}))
}
window.onload = function(){
loadApp({
templates:
[
'/javascripts/views/templates/nav-bar.handlebars',
],
scripts:
[
'https://maps.googleapis.com/maps/api/js?sensor=false&callback=initializeGoogleMaps',
'/javascripts/lib/bootstrap.js',
'/javascripts/lib/rails.js',
'/javascripts/lib/ember.js',
'/javascripts/application.js',
'/javascripts/views/nav_bar.js',
]
})
}
EDIT: I cleaned it up and made it work properly only testing in chrome though
It is possible, but you will need to precompile your templates first. This will also allow you to include all your templates within one file.
If you are using Rails, you can use the ember-rails gem to do it for you.
Otherwise you can follow these instructions on how to precompile handlebars
Later on, you will need to include the javascript file.
<script src="path/to/compiled/templates.js" type="text/javascript"></script>
If you load your templates into the DOM before loading Ember, then you don't need to compile or register your templates. Ember will come along after and do it for you.
Here is an article showing how:
http://weboriented.wordpress.com/2012/09/02/loading-handlebars-templates-in-ember-using-curl-js/
Here's another solution, inside your Ember view/component:
var viewClass = Ember.View.extend({ templateName: this.get('contentTemplate') });
var view = this.createChildView(viewClass);
var html = view.renderToBuffer().buffer;
This is an old question, so all the answers are a bit outdated. With Ember CLI templates are auto loaded by naming convention as require.js modules. It's a bit odd, since you write proposed ES6 import syntax and the build transpiles it into require.js syntax, but it works really well.
It is possible, and yes, you can do it without usage of another another another tool, just using ember.js and nothing else. i did it like this:
1) html code. note that all handlebars files need to be loaded before using any of them. here, its just one file named handlebars.js
<body>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script>window.jQuery || document.write('<script src="js/libs/jquery-1.7.2.min.js"><\/script>')</script>
<script src="js/libs/ember-0.9.8.1.min.js"></script>
<script src="js/handlebars.js"></script>
<script src="js/app.js"></script>
</body>
2) this file handlebars.js contains the following, using the compiler of ember
var src = "Hello, <b>{{name}}</b>";
Em.TEMPLATES["say-hello"] = Em.Handlebars.compile(src);
3) and then inside your app.js file, just use it as it were available (which it is):
var hu = Em.View.create({
templateName: "say-hello",
name: "Allô",
mouseDown: function() {
window.alert("hello world!");
}
});
hu.appendTo("body");

Use of functions in Facebook's JavaScript SDK

I got the Facebook login/logout functionality to work, but had to do it with this un-elegant code like this:
<script type="javascript">
function loadfb() {
var e = document.createElement('script'); e.async = true;
e.src = document.location.protocol + '//connect.facebook.net/es_LA/all.js';
document.getElementById('fb-root').appendChild(e);
};
</script>
<body onload="loadfb()">
I plan on putting that Facebook login button on all the pages of the site, so I really don't want to have this function called onload of every page.
Is this function necessary? I don't completely understand what it is for. What is a better way to do this so I can take it out of my onload?
Thanks!
This code is for asynchronous loading of the Facebook JavaScript SDK. What it does is create the tag
<script async scr="https://connect.facebook.net/es_LA/all.js" ></script>
inside the <div id="fb-root"></div> element. While loading the SDK asynchronously is considered better practice, you can leave out this code and manually enter the script tag yourself - eg:
<div id="fb-root"></div>
<script src="http://connect.facebook.net/es_LA/all.js"></script>
<script>
FB.init({
...
});
</script>