Assign value to a locals variable in keystonejs - javascript

In keystonejs (a node cms), I am using handlebars as templating engine in keystonejs. In handlebar file, I am using #each to iterate through an array.
{{#each data.positions}}
<h1 onclick = "callFunction()"> {{title}} </h1>
{{/each}}
Here I want to call a function within route, How can I do it?
Other way that is coming in my mind to to initialize locals variable. Like I have a variable locals.selectedPositionIndex in route file and I want to assign #index value of specific h1 element to this variable when any h1 element is clicked.

You could include a script at the bottom of your Handlebars template and include the function there:
<script>
document.querySelector('h1').addEventListener('click', function() {
// Code here
})
</script>
Alternatively, there is a question here with a useful answer:
Passing a function into a Handlebars template
I have amended the answer from the above question to work with KeystoneJS:
In your view you can assign your function to locals:
locals.func = function () {
// Code here
};
Then you will need to create a Handlebars helper in /templates/views/helpers/index.js:
_helpers.stringifyFunc = function(fn) {
return new hbs.SafeString("(" + fn.toString().replace(/\"/g,"'") + ")()");
};
You can then use this in your Handlebars template:
<h1 onclick="{{ stringifyFunc func }}">{{ title }}</h1>
I hope this helps.

Related

Update a template with Mustache js

I`m using mustache js to render a template with data from API and works nice, but i need to update (re-render) same template after a while. In my case I have a list in template like this:
template.html
<div id="template">
{{#list}}
<span>{{firstName}} {{lastName}} - {{phone}}</span>
{{/list}}
</div>
index.js
$(document).ready(function(){
$.ajax(
//some ajax here
).done(function(response){
loadTemplate(response);
});
});
function loadTemplate(data){
var template = $("#template").html();
Mustache.parse(template);
var render = Mustache.to_html(template, data);
$("#template").empty().html(render);
};
But the user can add more elements on this list, and after that I need to update the mustache template. I tried call the Ajax (that response with new value add on the list) then call loadTemplate function again but does not work, the list does not change (update) with new values.
The first time you render the template, the original mustache template gets lost.
ONLY the rendered text exists in same location. So the second time you try to re-render the template there is no template to render simply text that is NOT a template anymore so the text is simply outputed again.
The solution is to store your original template in another location (eg inside an element with id=#originalTemplate).
Then do following:
function loadTemplate(data){
var template = $("#originalTemplate").html(); // NOTE we use original template which does not get overriden
Mustache.parse(template);
var render = Mustache.to_html(template, data);
$("#template").empty().html(render);
};

Backbone.js & Handlebars.js to create a relative URL

In flask, to create a url I use {{ url_for('login') }} in the template (Jinja2) and it returns whatever the url is associated the name login (for example /auth/login/). How do I do this in Handlebars.js and Backbone.js? Is it already implemented?
What I want to achieve:
{{#urlfor}}loginRoute{{/urlfor}}
and for that to return:
/auth/login.
My routes:
routes: {
'': 'indexRoute',
'auth/login': 'loginRoute'
}
I cannot make sense of why you would be wanting to do this. All you would be doing is creating a Handlebars helper for finding and returning the name of a key of some Object that holds some value:
The helper would be simple enough:
Handlebars.registerHelper('urlfor', function (value) {
for (var key in routes) {
if (routes.hasOwnProperty(key) && routes[key] === value) {
return key;
}
}
return null;
});
(This assumes, of course, that the helper has access to routes in its lexical scope.)
You would use this helper in a template in the following way:
<script id="Template" type="text/template">
<p>The URL for loginRoute is {{urlfor 'loginRoute'}}</p>
</script>
However, it seems to me that it would make more sense to forego the helper and instead do this computation before executing your template function and simply pass the value as a parameter:
template({ loginUrl: getUrlFor('loginRoute') });

In Meteor, the old way to define a helper function is not conflict to the new way?

I am new to Meteor and tried to follow the "Your First Meteor Application":
http://meteortips.com/first-meteor-tutorial/
I tried to define a helper function.
In the html file I wrote:
<head>
<title>Leaderboard</title>
</head>
<body>
<h1>Leaderboard</h1>
{{> leaderboard}}
</body>
<template name="leaderboard">
<!-- Hello World -->
<!-- {{player}} -->
<!-- {{otherHelperFunction}} -->
<ul>
{{#each player}}
<li>{{name}}:{{score}}</li>
{{/each}}
</ul>
{{numOfPlayer}}
</template>
And in the JS file I wrote:
if(Meteor.isClient){
Template.leaderboard.helpers({
"player": function(){
// return "Some other text";
return PlayersList.find();
},
"numOfPlayer": function(){
// return "Some other text";
return PlayersList.find().count();
},
"otherHelperFunction": function(){
return "Some other funciton";
}
})
Template.leaderboard.player = function(){
return "Some other text";
}
// console.log("Hello client");
}
if(Meteor.isServer){
console.log("Hello server");
}
PlayersList = new Mongo.Collection('players');
So here in the JS file for the client part, I defined two "player" helper functions: one in the old way and one in the new way. The old way is actually that I forgot to comment out but when I run this project, the website turned out to be executed in the "new way" and seemed that the old-way defined "player" helper function did not influence the project at all and the compiler did not say that there is any error or ambiguity (since you can see these two "player" helper function are defined for different functionalities). What is the reason for this? Is it because that the old-way defined helper function is overwritten by the new helper function?
This is the output interface.
From the Meteor source code:
The first identifier in a path is resolved in one of two ways:
Indexing the current data context. The identifier foo refers to the foo property of the current data context object.
As a template helper. The identifier foo refers to a helper function (or constant value) that is accessible from the current template.
Template helpers take priority over properties of the data context.
Your Template.leaderboard.player function definition is on the data context, therefore Blaze looks for a template helper first. Since you have the helper defined, it takes precedence and gets executed.
It is because the new way overrides the old way. New way assigns a new function reference the variable 'player'.

Why can't I call my Handlebars partial inside #each?

While refactoring some code I got this problem: When applying a Handlebars template that uses a partial, it complains about You must pass a string or Handlebars AST to Handlebars.compile. You passed function .... The function is this:
function (context, options) {
options = options || {};
var namespace = options.partial ? options : env,
helpers,
partials;
if (!options.partial) {
helpers = options.helpers;
partials = options.partials;
}
var result = templateSpec.call(
container,
namespace, context,
helpers,
partials,
options.data);
if (!options.partial) {
checkRevision(container.compilerInfo);
}
return result;
}
What I did:
A partial is used to iterate over a list (called members) to build a ul of checkboxes. I found out I needed to make a list of lists and put the call to the partial inside an #each and updated the template input.
I have this:
Template:
...
{{#each hierarchy.levels}}
<ul>
{{> mypartial}}
</ul>
{{/each}}
Partial (simplified):
{{#each members}}
<li>{{this.id}}</li>
{{/each}}
I've checked that each hierarchy.levels in my data structure has a members list.
If I replace the #each in my template with #with hierarchy.levels.[0] (for instance) it works but it won't work when iterating over levels.
I solved it. One of the 2nd level lists were empty (ie zero items) which led to Handlebars rendering it as an empty string which again led to Handlebars thinking that something was false and trying to render the partial as a regular template.

Passing variables through handlebars partial

I'm currently dealing with handlebars.js in an express.js application. To keep things modular, I split all my templates in partials.
My problem: I couldn't find a way to pass variables through an partial invocation. Let's say I have a partial which looks like this:
<div id=myPartial>
<h1>Headline<h1>
<p>Lorem ipsum</p>
</div>
Let's assume I registered this partial with the name 'myPartial'. In another template I can then say something like:
<section>
{{> myPartial}}
</section>
This works fine, the partial will be rendered as expected and I'm a happy developer. But what I now need, is a way to pass different variables throught this invocation, to check within a partial for example, if a headline is given or not. Something like:
<div id=myPartial>
{{#if headline}}
<h1>{{headline}}</h1>
{{/if}}
<p>Lorem Ipsum</p>
</div>
And the invokation should look something like this:
<section>
{{> myPartial|'headline':'Headline'}}
</section>
or so.
I know, that I'm able to define all the data I need, before I render a template. But I need a way to do it like just explained. Is there a possible way?
Handlebars partials take a second parameter which becomes the context for the partial:
{{> person this}}
In versions v2.0.0 alpha and later, you can also pass a hash of named parameters:
{{> person headline='Headline'}}
You can see the tests for these scenarios: https://github.com/wycats/handlebars.js/blob/ce74c36118ffed1779889d97e6a2a1028ae61510/spec/qunit_spec.js#L456-L462
https://github.com/wycats/handlebars.js/blob/e290ec24f131f89ddf2c6aeb707a4884d41c3c6d/spec/partials.js#L26-L32
Just in case, here is what I did to get partial arguments, kind of. I’ve created a little helper that takes a partial name and a hash of parameters that will be passed to the partial:
Handlebars.registerHelper('render', function(partialId, options) {
var selector = 'script[type="text/x-handlebars-template"]#' + partialId,
source = $(selector).html(),
html = Handlebars.compile(source)(options.hash);
return new Handlebars.SafeString(html);
});
The key thing here is that Handlebars helpers accept a Ruby-like hash of arguments. In the helper code they come as part of the function’s last argument—options— in its hash member. This way you can receive the first argument—the partial name—and get the data after that.
Then, you probably want to return a Handlebars.SafeString from the helper or use “triple‑stash”—{{{— to prevent it from double escaping.
Here is a more or less complete usage scenario:
<script id="text-field" type="text/x-handlebars-template">
<label for="{{id}}">{{label}}</label>
<input type="text" id="{{id}}"/>
</script>
<script id="checkbox-field" type="text/x-handlebars-template">
<label for="{{id}}">{{label}}</label>
<input type="checkbox" id="{{id}}"/>
</script>
<script id="form-template" type="text/x-handlebars-template">
<form>
<h1>{{title}}</h1>
{{ render 'text-field' label="First name" id="author-first-name" }}
{{ render 'text-field' label="Last name" id="author-last-name" }}
{{ render 'text-field' label="Email" id="author-email" }}
{{ render 'checkbox-field' label="Private?" id="private-question" }}
</form>
</script>
Hope this helps …someone. :)
This can also be done in later versions of handlebars using the key=value notation:
{{> mypartial foo='bar' }}
Allowing you to pass specific values to your partial context.
Reference: Context different for partial #182
This is very possible if you write your own helper. We are using a custom $ helper to accomplish this type of interaction (and more):
/*///////////////////////
Adds support for passing arguments to partials. Arguments are merged with
the context for rendering only (non destructive). Use `:token` syntax to
replace parts of the template path. Tokens are replace in order.
USAGE: {{$ 'path.to.partial' context=newContext foo='bar' }}
USAGE: {{$ 'path.:1.:2' replaceOne replaceTwo foo='bar' }}
///////////////////////////////*/
Handlebars.registerHelper('$', function(partial) {
var values, opts, done, value, context;
if (!partial) {
console.error('No partial name given.');
}
values = Array.prototype.slice.call(arguments, 1);
opts = values.pop();
while (!done) {
value = values.pop();
if (value) {
partial = partial.replace(/:[^\.]+/, value);
}
else {
done = true;
}
}
partial = Handlebars.partials[partial];
if (!partial) {
return '';
}
context = _.extend({}, opts.context||this, _.omit(opts, 'context', 'fn', 'inverse'));
return new Handlebars.SafeString( partial(context) );
});
Sounds like you want to do something like this:
{{> person {another: 'attribute'} }}
Yehuda already gave you a way of doing that:
{{> person this}}
But to clarify:
To give your partial its own data, just give it its own model inside the existing model, like so:
{{> person this.childContext}}
In other words, if this is the model you're giving to your template:
var model = {
some : 'attribute'
}
Then add a new object to be given to the partial:
var model = {
some : 'attribute',
childContext : {
'another' : 'attribute' // this goes to the child partial
}
}
childContext becomes the context of the partial like Yehuda said -- in that, it only sees the field another, but it doesn't see (or care about the field some). If you had id in the top level model, and repeat id again in the childContext, that'll work just fine as the partial only sees what's inside childContext.
The accepted answer works great if you just want to use a different context in your partial. However, it doesn't let you reference any of the parent context. To pass in multiple arguments, you need to write your own helper. Here's a working helper for Handlebars 2.0.0 (the other answer works for versions <2.0.0):
Handlebars.registerHelper('renderPartial', function(partialName, options) {
if (!partialName) {
console.error('No partial name given.');
return '';
}
var partial = Handlebars.partials[partialName];
if (!partial) {
console.error('Couldnt find the compiled partial: ' + partialName);
return '';
}
return new Handlebars.SafeString( partial(options.hash) );
});
Then in your template, you can do something like:
{{renderPartial 'myPartialName' foo=this bar=../bar}}
And in your partial, you'll be able to access those values as context like:
<div id={{bar.id}}>{{foo}}</div>
Yes, I was late, but I can add for Assemble users: you can use buil-in "parseJSON" helper http://assemble.io/helpers/helpers-data.html. (Discovered in https://github.com/assemble/assemble/issues/416).
Not sure if this is helpful but here's an example of Handlebars template with dynamic parameters passed to an inline RadioButtons partial and the client(browser) rendering the radio buttons in the container.
For my use it's rendered with Handlebars on the server and lets the client finish it up.
With it a forms tool can provide inline data within Handlebars without helpers.
Note : This example requires jQuery
{{#*inline "RadioButtons"}}
{{name}} Buttons<hr>
<div id="key-{{{name}}}"></div>
<script>
{{{buttons}}}.map((o)=>{
$("#key-{{name}}").append($(''
+'<button class="checkbox">'
+'<input name="{{{name}}}" type="radio" value="'+o.value+'" />'+o.text
+'</button>'
));
});
// A little test script
$("#key-{{{name}}} .checkbox").on("click",function(){
alert($("input",this).val());
});
</script>
{{/inline}}
{{>RadioButtons name="Radio" buttons='[
{value:1,text:"One"},
{value:2,text:"Two"},
{value:3,text:"Three"}]'
}}

Categories