Given HTML code such :
<!-- 2. Anchor -->
<div id="anchor">This div is the <b>#anchor</b>.</div>
<!-- 3. Template -->
<script id="tpl" type="text/template">
{{#people}}
<div><img src="{{photo}}"><b>{{family}} {{name}}</b> — {{title}}, {{place}} : {{introduction}}.</div>
{{/people}}
</script>
Given JS/Handlebars such as :
<!--4. Handlebars.js slingshot -->
//4a.function creation
var slingshot = function (url, tplId, anchor) {
$.getJSON(url, function(data) {
var template = $(tplId).html();
var stone = Handlebars.compile(template)(data);
$(anchor).append(stone);
});
}
slingshot('data.json', '#tpl', '#anchor'); // since url = 'data.json' , we can use both notations.
How to externalize the my 3. Template (#tpl) into a proper .txt text file (or other extension) ? How to load it back ? so I may use the same template into various .html webpages.
Full code : http://bl.ocks.org/hugolpz/8075193 / http://bl.ocks.org/hugolpz/raw/8075193/
Put the following template content into a file named test.handlebars
{{#people}}
<div><img src="{{photo}}">
<b>
{{family}} {{name}}
</b> — {{title}},
{{place}} : {{introduction}}.
</div>
{{/people}}
Write a function which will use the template as below
function getTemplate(name) {
if (Handlebars.templates === undefined || Handlebars.templates[name] === undefined) {
$.ajax({
url : name + ".handlebars",
success : function(data) {
if (Handlebars.templates === undefined) {
Handlebars.templates = {};
}
Handlebars.templates[name] = Handlebars.compile(data);
},
async : false
});
}
return Handlebars.templates[name];
}
In the main program you can write the below statement to insert the template contents into div with id="anchor", as shown below
var Template = getTemplate("test")
this.$("#anchor).append(Template(data));
where data is the contents of a json file or some db query output which will give you the values meant for the following attributes in json format
people, twitter, name, family, photo, title, place, introduction
I'm assuming you have already compiled your template. So you can use the technique I have described in Bootstrapping Multiple Instances of an HandlebarsJS Template Into a Page.
Hook and libs
Place this in your index.html:
<div class="hook" data-json="data/whatever.json"></div>
and the JavaScript libs
<!-- Helper to inject data-set in templates instance -->
<script src="scripts/template-loader.js"></script>
<!-- Get the (compiled) template -->
<script src="scripts/myTemplate.hbs.js"></script>
template-loader.js helper
$(function(){
'use strict';
var compiledTemplate = myApp.Templates['app/templates/myTemplate.hbs'];
$('.hook').each(function(i, h){ # h = current hook
var url = $(h).data('json'); # data-set's url
$.getJSON(url).then(function (json) { # fetch data-set
var tpl = compiledTemplate( json ); # inject data into template
$(h).html(tpl); # inflate template in page
});
});
});
Please read the complete article for further details.
Related
I've got an MJML template which compiles to HTML, and am wanting to load variables from a JSON file to the template. I'm new to HTML, JS & Handlebars so could be really off track here.
My MJML template, test.mjml, looks something like:
<mjml>
<mj-body>
<mj-raw><script type="text/javascript" src="handlebars-v4.7.3.js"></script></mj-raw>
<mj-raw><script type="text/javascript" src="http://code.jquery.com/jquery-latest.min.js"></script></mj-raw>
<mj-section>
<mj-text><mj-raw>
<script id="test-template" type="text/x-handlebars-template">
<div>
<p>
Hello my name is {{name}}.
</p>
</div>
</script>
</mj-raw></mj-text>
</mj-column>
</mj-section>
I have a JSON file, data.json:
{
"name": "John",
"height": "175cm",
"occupation": "Teacher",
}
And a JS file, test.js:
$(document).ready(function() {
var template = $('#test-template').html();
var compiledTemplate = Handlebars.compile(template);
$.getJSON('data.json', function (data) {
var context = data;
}
});
var html = compiledTemplate(context);
$(document.body).append(html);
Does anyone know how to do this / what I'm doing wrong?
If I replace jQuery's getJSON method with an inline JSON, it works.
I have one handlebars template but I want to include variables from two different sources in this template.
<script id="notification-menu-item" type="text/x-handlebars-template">
I have tried to make both of the sources go to the same template id. Both files have this:
var source = $("#notification-menu-item").html();
var template = Handlebars.compile(source);
But only one of sources' variable come through to the template. Is there anyway to have one template get its {{variables}} from two different sources?
Edit: The code
This is the template:
<script id="notification-menu-item" type="text/x-handlebars-template">
<div id="navmenucontainer" class="container">
<div id="navmenuv">
<ul class="nav">
<li>Topics</li>
<li>Help</li>
{{#if logged_user}}
<li>Notifications</li>
{{#if pro}}
<li>My Data</li>
{{/if}}
{{/if}}
</ul>
</div>
</div>
</script>
pro comes from one .js file and logged_user comes from a separate .js file. Is there a way for both of these variable to be used in the same template?
You'll have to centralize the rendering of the template into one function somehow if you want to composite the data before passing it into the Handlebars.compile() function. I guess you're going to have to somehow guarantee the order in which these "plugin" js files call this new function. Otherwise it turns into something really janky like this:
Example:
Class1.js
var source = $("#notification-menu-item").html();
var html = Notification.renderNotification(source, logged_user, undefined);
if (typeof html !== 'undefined') {
$('body').prepend(html);
}
Class2.js
var source = $("#notification-menu-item").html();
var html = Notification.renderNotification(source, undefined, pro);
if (typeof html !== 'undefined') {
$('body').prepend(html);
}
Notification.js
window.Notification = function() {
var logged_user = undefined;
var pro = undefined;
return {
renderNotification: function(source, user, isPro) {
if (typeof user !== 'undefined') {
logged_user = user;
}
if (typeof pro !== 'undefined') {
pro = isPro;
}
if(typeof logged_user !== 'undefined'
&& typeof pro !== 'undefined') {
var template = Handlebars.compile(source);
var html = template({logged_user: logged_user, pro: pro});
return html;
}
}
}
Obviously this is not elegant and far from maintainable. Without getting into the specifics of how Discourse works though, I'm not sure what to tell you. At render time of the template, a full object containing all the relevant data should be passed. Subsequent calls to Handlebars.compile() would require the full set of data. Maybe should consider finding a way to split these templates up and render them into separate page elements asynchronously, or look into Partials
Disclaimer: I'm not an expert on JS or logicless templates.
In order to write the HTML code of social icons (Twitter, Linkedin, etc) to a textarea so that the user can use that code elsewhere, I would like to get the HTML code of the view element, but I'm having some issues. To help illustrate this better, here is the code that creates the view:
define(function(require, exports, module) {
var _ = require('underscore');
var GridControlView = require('pb/views/grid-control');
var SocialiconsControlDialog = require('pb/views/socialicons-control-dialog');
var template = require('text!pb/templates/socialicons-grid-control.html');
var SocialiconsGridControlView = GridControlView.extend({
template: _.template(template)
,templateVars: {
partials: {
facebook: require('text!pb/templates/socialicons-grid-control-facebook.html')
,twitter: require('text!pb/templates/socialicons-grid-control-twitter.html')
,googleplus: require('text!pb/templates/socialicons-grid-control-googleplus.html')
,pinterest: require('text!pb/templates/socialicons-grid-control-pinterest.html')
,linkedin: require('text!pb/templates/socialicons-grid-control-linkedin.html')
}
}
,control_dialog: SocialiconsControlDialog
});
return SocialiconsGridControlView;
});
And, for example, the Linkedin template looks like this:
<script src="//platform.linkedin.com/in.js?<%- t.cache_buster %>" type="text/javascript">lang: en_US</script>
<script type="IN/Share" data-counter="<%- t.linkedin_option_countmode %>"></script>
What I would like to retrieve, is the parsed template code as text, something such as:
<script type="text/javascript" src="//platform.linkedin.com/in.js?0.4670609195438331">
<script data-counter="top" type="IN/Share+init">
But using something such as:
control_view.render().$el.innerHTML;, control_view.render().$el.html().text() or control_view.render().$el.html().replace(/<\/?[a-z][a-z0-9]*[^<>]*>/ig, ""); doesn't return text; it returns the full HTML, and produces a Linkedin icon (when I just want the text to be written to a textarea).
Any thoughts?
Update **
I noticed that the code control_view.render().$el is working correctly on other places of the application, and returning HTML code, but for some reason in this view where I'm trying it doesn't. The code seems to break at:
$control = control_view.render().el;
and in the console I get an error which is:
TypeError: t is undefined - underscore-min.js (line 3)
Use the .outerHTML property of the $el.
var html = $('<script type="text/javascript" src="//platform.linkedin.com/in.js?0.4670609195438331">' +
'<script data-counter="top" type="IN/Share+init">');
var text = html[0].outerHTML;
$('textarea').val(text);
jsFiddle
I'm using handlebars.js on a project and I'm starting to have a fair amount of templates.
For now they are stored in my main template app file, like this :
<script id="avatar_tpl" type="text/html">
bla bla bla {{var}} bla bla bla
</script>
I'm wondering if there is a way to put them in a separate file like a .js file or something, to avoid stacking them up in my source code page.
I'm aware that there are several solutions to call theses templates via Ajax, but that seems to result in too much unnecessary requests for me.
Thank you
I created and open-sourced NodeInterval for this exact same problem of too many js templates in my HTML page.
It allows you to put all your templates into a templates folder organized in whatever hierarchy you like. It has a built in watch capability so that as you modify any of these templates it automatically updates your HTML page. I use it alongside SASS for my CSS.
I use it daily with underscore templates but it should work fine with moustache templates as well:
https://github.com/krunkosaurus/NodeInterval
Couldn't you just include a js file with your templates as js variables? Not tested, just thinking here:
//in your html page
<script id="avatar_tpl" type="text/html" src="mytemplates.js"></script>
// then in your mytemplates.js file
var template_1 = "{{ content }}";
var template_2 = "{{ content }}";
// and you could use it like this back in html page
var template1 = Handlebars.compile(template_1);
var template2 = Handlebars.compile(template_2);
if you are using jquery, you could create an invisible div with id "template-holder"
then use :
$("#template-holder").load([url here])
to load the html into the div
then use :
var templatestr = $("#template-holder").find("#avatar_tpl").html()
to get the template
:)
I'm not familiar with handlebars.js but, have you tried this?:
<script id="avatar_tpl" type="text/html" src="myscript.html"></script>
I've been rolling all my scripts and templates in to one big .js file for several projects now. I use a java-based build tool, ant, to concatenate and manage various processing scripts for my js.
The biggest problem with storing large templates in javascript variables is javascript's lack of multi-line strings. I deal with this by writing my files with a python-like triple-quote syntax:
var templateVariable = '''
<div>
<div></div>
</div>
'''
I then run this custom-syntax javascript file though the python script included below, which turns it in to legal javascript:
#!/usr/bin/env python
# encoding: utf-8
"""
untitled.py
Created by Morgan Packard on 2009-08-24.
Copyright (c) 2009 __MyCompanyName__. All rights reserved.
"""
import sys
import os
def main():
f = open(sys.argv[1], 'r')
contents = f.read()
f.close
split = contents.split("'''")
print "split length: " + str(len(split))
processed = ""
for i in range(0, len(split)):
chunk = split[i]
if i % 2 == 1:
processedChunk = ""
for i,line in enumerate(chunk.split("\n")):
if i != 0:
processedChunk = processedChunk + "+ "
processedChunk = processedChunk + "\"" + line.strip().replace("\"", "\\\"").replace('\'', '\\\'') + "\"" + "\n"
chunk = processedChunk
processed = processed + chunk
f = open(sys.argv[1], 'w')
f.write(processed)
f.close()
if __name__ == '__main__':
main()
Working this way, I can code templates in more-or-less pure html, and deploy them, along with application code, inside a single .js file.
I created a Lazy Load javascript file that loads the templates only as needed. It's performing the AJAX calls, but seems to work quite well.
var Leuly = Leuly || {};
Leuly.TemplateManager = (function ($) {
var my = {};
my.Templates = {};
my.BaseUrl = "/Templates/";
my.Initialize = function (options) {
/// <summary>
/// Initializes any settings needed for the template manager to start.
/// </summary>
/// <param name="options">sets any optional parameters needed</param>
if (options && options.BaseUrl) {
my.BaseUrl = options.BaseUrl;
}
};
my.GetTemplate = function (templateName, success, baseUrl) {
/// <summary>
/// makes a request to retrieve a particular template
/// </summary>
/// <param name="templateName">name of the template to retrieve</param>
/// <param name="success">event returning the success</param>
var template = my.Templates[templateName];
if (template == null) {
template = my.LoadTemplate(templateName, success, baseUrl);
}
else {
success(template, true);
}
};
my.LoadTemplate = function (templateName, success, baseUrl) {
/// <summary>
/// makes a request to load the template from the template source
/// </summary>
/// <param name="templateName">name of the template to retrieve</param>
/// <param name="success">event returning the success</param>
var root = baseUrl == null ? my.BaseUrl : baseUrl;
$.get(root + templateName, function (result) {
my.Templates[templateName] = result;
if (result != null && success != null) {
success(result, true);
}
});
};
return my;
} (jQuery));
$(function () {
Leuly.TemplateManager.Initialize();
});
I created a helper in Handlebars to help with logic, but my template parses the returned html as text rather than html.
I have a quiz results page that is rendered after the quiz is completed:
<script id="quiz-result" type="text/x-handlebars-template">
{{#each rounds}}
{{round_end_result}}
{{/each}}
<div class="clear"></div>
</script>
For each of the rounds, I use a helper to determine which template to render a round's result:
Handlebars.registerHelper("round_end_result", function() {
if (this.correct) {
var source = '';
if (this.guess == this.correct) {
console.log("correct guess");
var source = $("#round-end-correct").html();
} else {
var source = $("#round-end-wrong").html();
}
var template = Handlebars.compile(source);
var context = this;
var html = template(context);
console.log(html);
return html;
} else {
console.log("tie");
}
});
Here is a template that describes a correct round (let's take say it rendered the #round-end-correct template):
<script id="round-end-correct" type="text/x-handlebars-template">
<div></div>
</script>
Here is what gets rendered:
<div></div>
Not as HTML, but as text. How do I get it to actually render the HTML as HTML, rather than text?
I assume that unescaping in Handlebars works the same as in vanilla Mustache.
In that case use triple mustaches to unescape html, i,e: {{{unescapedhtml}}}, like:
<script id="quiz-result" type="text/x-handlebars-template">
{{#each rounds}}
{{{round_end_result}}}
{{/each}}
<div class="clear"></div>
for ref see:
http://mustache.github.com/mustache.5.html
Geert-Jan's answers is correct but just for reference you can also set the result to "safe" directly inside the helper (code from handlebars.js wiki)
Handlebars.registerHelper('foo', function(text, url) {
text = Handlebars.Utils.escapeExpression(text);
url = Handlebars.Utils.escapeExpression(url);
var result = '' + text + '';
return new Handlebars.SafeString(result);
});
With that you can use regular double handlebars {{ }} and handlebars won't escape your expression.