I have an inline template (template in JavaScript) that I used to compile like so (where temp is a Handlebars string):
var template = Handlebars.compile(temp);
template({model: results}); // Gets the HTML
I'm trying to use HTMLBars instead, but can't quite figure it out. I did the following:
var template = Ember.HTMLBars.compile(temp);
template({model: results}); // Throws an error that template isn't a function
How do I go about getting the HTML back from the HTMLBars template. I also tried:
var template = Ember.HTMLBars.compile(temp);
Ember.HtmlBars.template(template, {model: results});
Which doesn't error, but also doesn't use the model when rendering the HTML. I think I'm close, but can't quite figure out how to inject the model.
HTMLBars doesn't produce functions like Handlebars did. Handlebars was a string templating language: you compile a string to a template function, then run that function to produce a string. HTMLBars compiles a string into a template but the template doesn't produce a string, it produces DOM nodes. The simple answer is that you're not going to be able to do the same things with HTMLBars as you did with Handlebars. I suggest either adding another string templating library to your code (maybe Handlebars), or letting a view handle the HTMLBars template as seen in this question.
And if you're curious, here's what an HTMLBars template object looks like after being compiled (gotten from a JSBin console dump):
[object Object] {
blockParams: 0,
build: function build(dom) {
var el0 = dom.createDocumentFragment();
var el1 = dom.createTextNode("");
dom.appendChild(el0, el1);
var el1 = dom.createTextNode("");
dom.appendChild(el0, el1);
return el0;
},
cachedFragment: null,
hasRendered: false,
isHTMLBars: true,
isMethod: false,
isTop: true,
render: function render(context, env, contextualElement) {
var dom = env.dom;
var hooks = env.hooks, content = hooks.content;
dom.detectNamespace(contextualElement);
var fragment;
if (env.useFragmentCache && dom.canClone) {
if (this.cachedFragment === null) {
fragment = this.build(dom);
if (this.hasRendered) {
this.cachedFragment = fragment;
} else {
this.hasRendered = true;
}
}
if (this.cachedFragment) {
fragment = dom.cloneNode(this.cachedFragment, true);
}
} else {
fragment = this.build(dom);
}
if (this.cachedFragment) { dom.repairClonedNode(fragment,[0,1]); }
var morph0 = dom.createMorphAt(fragment,0,1,contextualElement);
content(env, morph0, context, "foo");
return fragment;
}
}
Related
I'm working on a Flask app where some data gets passed to a template, and I want to make that data available to multiple instances of an object. Here's what it would look like if I just hardcoded the desired data into my .js file:
var Module = function(selector) {
var targetDiv = selector,
targetData = 'lorem ipsum sit dolor',
show = function() {
$('<p>' + targetData + '</p>').appendTo(targetDiv);
};
return {
show: show,
};
};
$(document).ready(function() {
firstModule = new Module($('#first'));
secondModule = new Module($('#second'));
firstModule.show();
secondModule.show();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id='first'></div>
<div id='second'></div>
I can't just call a function on an unconstructed Module object, so my next step is to create a static-like ModuleFactory that I can load with data from jinja, and then create Modules from there:
var ModuleFactory = function() {
var targetData = null,
setData = function(data) {
targetData = data;
},
create = function(selector) {
return new Module(selector, data);
};
return {
setData: setData,
create: create,
};
} ();
Then I attempt to call ModuleFactory.setData({{data}}); from a <script> tag in the HTML, and do something like ModuleFactory.create($('#first')).show(); in the .js
But of course because I have to include my .js file before using the ModuleFactory in the HTML, I end up constructing the objects before the factory is initialized.
(Past this point, my workaround attempts stop being relevant to the problem.)
Anyway, what's the correct way of getting data from Jinja to my JS module? There has to be a common pattern or something.
This feels awful, but it works:
Have an object with a runProgram method, which takes the desired data as a parameter, then executes the logic that had previously been inside the document ready function. e.g.
var ProgramRunner = function() {
var runProgram = function(data) {
ModuleFactory.setData(data);
firstModule = ModuleFactory.create($('#first'));
firstModule.show();
};
return {
runProgram: runProgram;
};
};
Then just
<script>
$(document).ready(function() { ProgramRunner.runProgram({{data}}) });
</script>
in the HTML.
(Leaving question open, because I suspect there's a much better way of handling this.)
As per I know, marionette.template accept either jquery selector or compiled template string.
If I write code like in the following way, working fine
exports.ProductInfoView=Backbone.Marionette.ItemView.extend({
domInfo:{
mainTemplateId:"tagProductListTpl",
tableTemplateId:"taginfoViewTpl"
},
template:commomFunctions.templateCompilation("tagProductListTpl",""),
onRender:function(){
this.templatingProductInformation();
},
modelEvents:{
"change:currentJson":"templatingProductInformation"
},
templatingProductInformation:function(){
console.log(this.el);
//this.el.innerHTML=commomFunctions.templateCompilation(this.ui.mainTemplateId,"");
}
});
Note :commonFunctions.templateCompilation() accept templateId as first argument and data as second argument. It will compile handlebars template and it return compiled template.
If I assign that return value to template, working fine.
I want to make data for templating,so I am passing function to template like in the following way.
exports.ProductInfoView=Backbone.Marionette.ItemView.extend({
domInfo:{
mainTemplateId:"tagProductListTpl",
tableTemplateId:"taginfoViewTpl"
},
template:function(){
return commomFunctions.templateCompilation("tagProductListTpl","");
},
onRender:function(){
this.templatingProductInformation();
},
modelEvents:{
"change:currentJson":"templatingProductInformation"
},
templatingProductInformation:function(){
console.log(this.el);
//this.el.innerHTML=commomFunctions.templateCompilation(this.ui.mainTemplateId,"");
}
});
This way also working fine, If you observer I hard coded templateId("tagProductListTpl") inside function.But I don't want like that. I want to use like this.domInfo.mainTemplateId instead of hard coding. that way it's not working fine.
It's throwing error. I know it's out of scope. but how can I achive this.
can anyone help me.
Thanks.
I will advise you to rewrite Marionette.TemplateCache.prototype.compileTemplate which is responsible for template compilation. Look at this post, there almost the same issue.
Marionette.TemplateCache.prototype.compileTemplate = function (yourRawTemplate) {
// In case if template is function
if (_.isFunction(yourRawTemplate)) {
return yourRawTemplate;
} else {
return Handlebars.compile(yourRawTemplate);
}
};
And if you loading template files from remote server you also need to rewrite Backbone.Marionette.TemplateCache.prototype.loadTemplate. Here example:
Marionette.TemplateCache.prototype.loadTemplate = function ( templateId ) {
var template = '',
templateUrl = 'path_to_your_template/' + templateId + '.html';
// Loading template synchronously.
Backbone.$.ajax( {
async : false,
url : templateUrl,
success : function ( templateHtml ) {
template = templateHtml;
}
} );
return template;
};
I'm having troubles using variables that would normally be no problem with understand.js, but seemingly when you combine JST with underscore.js it seems to struggle.
var something= SD.defaultView.extend({
el: 'page',
template: JST['app/www/js/templates/sex.ejs'],
data: {
header: 'some information!!!',
image: '/img/path.jpg'
},
render: function () {
var compiled = _.template(this.template(), this.data); //I pass in the complied JST template
this.$el.html(compiled);
}
});
JST File rendered
this["JST"]["app/www/js/templates/sex.ejs"] = function (obj) {
obj || (obj = {});
var __t, __p = '', __e = _.escape;
with (obj) {
__p += ((__t = ( header )) == null ? '' : __t) + '<sexform>Hello There</sexform>';
}
return __p
};
Error
ReferenceError: header is not defined - templates.js (line 21)
...obj = {});var __t, __p = '', __e = _.escape;with (obj) {__p +=((__t = ( header )...
sex.ejs
<%= header %><sexform>Hello There</sexform>
Background Information
As expected, header is not available at the time of the reader, which is happening via grunt file with each change to my JST templates. I feel I must be implement JST's the wrong way.
But, to me this seems like the correct way of doing everything.
Of course, I am trying to use variables with underscore inside of sex.ejs
All of this code can be seen here: http://m.sexdiaries.co.uk/#wank
NB:
I can assure that this is safe for work and contains no images, even though as misleading as the url is its really not adult material, its an educational app.
You have this to define the view's template:
template: JST['app/www/js/templates/sex.ejs'],
And JST contains functions (which is, more or less, the whole point of using JST-style precompiled templates):
this["JST"]["app/www/js/templates/sex.ejs"] = function (obj) {
Then you do this:
var compiled = _.template(this.template(), this.data);
// function call ----------------------^^
Two things are wrong there:
You've already called _.template to compile the template.
this.template is the compiled template function that expects to be fed this.data.
The fix is quite simple:
var compiled = this.template(this.data);
I've seen a number of approaches to creating your own namespaces in JS, but it does get a bit confusing.
We make use of JQuery and in one project we recently got involved with I noticed that developers used this approach:
var myObject = new function ($){
var localVar = 1,
init = function(){
...
},
return {
init: init
}
}
Why the use of 'new' function
Why pass in the $ into this new function?
Why this form of return?
In another project we used the following approach:
$.companyName = {
textNotFound: "The search returned no results",
search: {
orders: null,
stock: null
}
}
$.companyName.search.orders = function(param1,...){
...
}
$.companyName.search.stock = function(param1,...){
...
}
The usage scenario would look like this:
var orders = $.companyName.search.orders(...);
if (orders == null){
alert($.companyName.textNotFound);
}
else{
...
}
Is this second method not advisable and the first should be used instead?
Or, should we be authoring JQuery plugins? (I wouldn't think so, because this is site specific functionality)
Thanks,
Jacques
I've started looking into using Knockout for my team's use; I've been very pleased with the framework so far, with the exception of its ties to the jQuery Templates plugin, whose syntax I hate. Additionally (and more importantly), we've been using jQote2 for our client-side templating, so I wanted to look into creating a template engine that uses it.
I'm having difficulty with the renderTemplate function; the jQote2 library doesn't seem to know how to locate the variables passed in through the data argument. Has anyone dealt with this sort of thing before? I'm assuming it's more of a jQote2 quirk than anything with how Knockout renders its templates...
The code:
jqoteTemplateEngine = function () {
var templates = {};
this.createJavaScriptEvaluatorBlock = function (script) {
return '<%= ' + script + ' %>';
}
this.getTemplateNode = function (template) {
var templateNode = document.getElementById(template);
if (templateNode == null)
throw new Error("Cannot find template with ID of " + template);
return templateNode;
}
this.renderTemplate = function(templateId, data, options) {
var renderedMarkup = jQuery.jqote(templates[templateId], data);
return ko.utils.parseHtmlFragment(renderedMarkup);
}
this.rewriteTemplate = function (template, rewriterCallback) {
var templateNode = this.getTemplateNode(template);
templates[template] = rewriterCallback(templateNode.text);
}
this.isTemplateRewritten = function (templateId) {
return templateId in templates;
}
};
jqoteTemplateEngine.prototype = new ko.templateEngine();
ko.setTemplateEngine(new jqoteTemplateEngine());
The Gist: http://gist.github.com/1341737
I would take a look at KO 1.3. It is in beta, but is quite stable. Two important things in 1.3. There is now a native template engine, so you could choose to avoid having any dependency on an external template engine (and even jQuery). The control-flow bindings with anonymous templates make this much simpler option.
Secondly, it simplified the way that custom template engines are defined. So, if you still want to use an external engine, then it is a bit easier to get it working.
Basically, you only need to define a renderTemplate method and a createJavaScriptEvaluatorBlock if you want to be able to data-bind to variables available to the template engine. The rest is defined on a ko.templateEngine base "class".
So, your engine might simply look like:
ko.jqoteTemplateEngine = function() {};
ko.jqoteTemplateEngine.prototype = new ko.templateEngine();
ko.jqoteTemplateEngine.prototype.createJavaScriptEvaluatorBlock = function(script) {
return '<%= ' + script + ' %>';
};
ko.jqoteTemplateEngine.prototype.renderTemplateSource = function(templateSource, bindingContext, options) {
var templateText = templateSource.text();
return ko.utils.parseHtmlFragment($.jqote(templateText, bindingContext.$data));
};
ko.setTemplateEngine(new ko.jqoteTemplateEngine());
Here is a sample in use: http://jsfiddle.net/rniemeyer/yTzcF/