Modifying and passing data through nested Handlebars partials - handlebars.js

How is it possible to modify data from a partial when passing down to its sub-partial (in the context of assemble.io)?
usage is something like this:
<!-- index.hbs -->
{{> heroImage src="img.jpg"}}
<!-- heroImage.hbs -->
<div>{{> responsiveImage srcset=src+"480w"}}</div>
<!-- responsiveImage.hbs -->
<img srcset={{srcset}} />
I can only pass the src data but not using and modifying it, like src + "something" or src"something".

If you wish to perform a string concatenation from within a mustache you will need to use a helper method and invoke it as a sub expression.
Such a helper method could be as simple as the following:
Handlebars.registerHelper('concat', function () {
return Array.prototype.slice.call(arguments, 0, -1).join('');
});
And your heroImage.hbs partial would use this helper in the following way:
<div>{{> responsiveImage srcset=(concat src " 480w")}}</div>
I have also created an example fiddle.

Related

How to override Handlebars' `if` helper

I'm looking for a way to change how Handlebars' if helper works.
Mandrill has made a few modifications to the standard Handlebars. One of these handy changes is in the way they handle Handlebars' if block helper. They've added the ability to evaluate expressions inside the if helper like this:
{{#if `purchases > 3`}}
I need to be able to compile Mandrill templates locally, for testing and preview, and this feature is making it difficult. I know I can write my own custom helpers, but in this case I'm looking for a way to alter the way the built-in if helper works.
I guess I could build my own version of Handlebars.
Any other suggestions?
I don't think that there is a problem to redefine the standard helpers as long as yours are working fine. You'll find in the below snippet one example that override the if helper to put instead a text. So just register your own helper this will override the default one. If you want the {{else}} also working you'll have to handle it in your if helper code.
$(document).ready(function () {
var context = {
"textexample1" : "hello",
};
Handlebars.registerHelper('if', function(text) {
return new Handlebars.SafeString(text);
});
var source = $("#sourceTemplate").html();
var template = Handlebars.compile(source);
var html = template(context);
$("#resultPlaceholder").html(html);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.5/handlebars.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script id="sourceTemplate" type="text/x-handlebars-template">
{{#if 'test override'}}
{{textexample1}}
{{/if}}
</script>
<br/>
<div id="resultPlaceholder">
</div>

How can I add a unique class and assign unique data to copies of the same template using Meteor, Blaze, Spacebars?

I'm trying to create a dynamic slider that can be reused with different images.
<div id="sly_container" class="sly">
<ul>
{{#each propertyImages}}
<li><img src="{{ImageURL}}"></li>
{{/each}}
</ul>
<button id="gallery_btn-prev"><img class="gallery_arrow"/>prev</button>
<span id="middleSpan"></span>
<button id="gallery_btn-next"><img class="gallery_arrow"/>next</button>
</div>
I populate propertyImages with an httpRequest (I make several of these):
(function(o){
HTTP.call( 'GET', 'https://api.rentcafe.com/rentcafeapi.aspx?requestType=images&type=propertyImages&companyCode=c00000084939&propertyCode='+v+'', {},
function( error, response ) {
if ( error ) {
console.log( error );
} else {
var content = JSON.parse(response["content"]);
obj[p] = content;
if( o == l){
CommunityImages.insert(obj)
Session.set('imagesLoaded',true);
}
}
console.log(v+ ' ' + p + ' ' + o + ' images delivered')
})
}(o++))
and then use this helper:
Template.sly.helpers({
propertyImages: function(){
if(Session.get('property') && CommunityImages.find().fetch()[0]){
return CommunityImages.find().fetch()[0][Session.get('property')]
}
})
After it renders I run some more logic on it to create the slider from the images. It works well when there is one slider per view, since it is dependent on the Session.set('property', 'whatever') but I want to have many on the same page each populated with different images. I can add keys and values to the image objects, so I suppose maybe I can do this with a Spacebars conditional? In the end I'd like to have something like this
<div id="summit-sly">{{> sly}}</div>
<div id="hillcroft-sly">{{> sly}}</div>
<div id="merrit_station-sly">{{> sly}}</div>
with each slider containing it's respective images, OR I'm now seeing that maybe partials could work somehow:
{{>sly summit}}
{{>sly hillcroft}}
{{>sly merrit_station}}
Each slider will basically need it's own class name, so that the logic that runs on render will target each one specifically and not all of them.
Indeed, you can use partials in Blaze spacebars with either:
A single argument that acts as the data context of the called template (as exemplified in Meteor Guide > View > User Interfaces > UI components > Smart components).
Named arguments (as shown in Meteor Guide > View > Blaze > Spacebars > Template inclusion), which are actually assembled into a data object (see below).
{{> subComponent arg1="value-of-arg1" arg2=helperThatReturnsValueOfArg2}}
Another tutorial: http://meteorcapture.com/spacebars/#template-inclusion-ex-2
Reference: Meteor API Docs > Packages > spacebars > Inclusion and Block Arguments
Inclusion tags ({{> foo}}) and block tags ({{#foo}}) take a single data argument, or no argument. Any other form of arguments will be interpreted as an object specification or a nested helper:
Object specification: If there are only keyword arguments, as in {{#with x=1 y=2}} or {{> prettyBox color=red}}, the keyword arguments will be assembled into a data object with properties named after the keywords.
Nested Helper: If there is a positional argument followed by other (positional or keyword arguments), the first argument is called on the others using the normal helper argument calling convention.
Then you retrieve the data context through the templateInstance.data property:
In an onCreated, onRendered or onDestroyed callback, the template instance is directly available in this.
In a helper or in the HTML part of the template, the data context is directly available in this (no need to look for its data child property). In a helper, you can also access the template through Template.instance().
In an event handler, the template instance is passed as the 2nd argument of the listener.

Length of json object in handlebars template

Is it possible to get the length of json object inside the handlebars template using the property .length .
{{json.length}}
If not is it possible to find the length based on the list of keys and then using the .length such as
{{json.keys.length}}
Sample JSON structure
{1232134235423:[Name,Destination,Desc],
2134213214321:[Name],
2342354356634:[Name,Desc]
}
Edit 1:I know this can be achieved by using a custom helper but this length has to be used inside custom if helper. So something like array.length would be useful
If I understand your edit right the problem is not to use another helper but to call it within another helper's call.
Thus, you could use a second helper nested in your custom if-helper call:
{{#customIf (objectLength json)}}
do some stuff...
{{/customIf}}
According to this answer "Getting JavaScript object key list" you can use Object.keys(json) to get all your object's keys.
The new helper objectLength could look like that:
Handlebars.registerHelper("len", function(json) {
return Object.keys(json).length;
});
You can check length of object in handlebars template like this in your template. So HTML will render only when the object have one or more child element.
{
people: [
"abc",
"Alan Johnson",
"Charles Jolley",
],
}
{{#if people.length}}
<p>{{ people}} </p>
<ul class="people_list">
{{#each people}}
<li>{{this}}</li>
{{/each}}
</ul>
{{/if}}

HandlebarsJS not outputting html template unless receives a param

I have this code:
if (this.template) {
var template = Handlebars.compile( $(this.template).html() );
$(this.el).html(template());
}
with this template:
<script id="tmpl-nav-account" type="text/x-handlebars-template">
{{#this}}
<div class="nav-account">
topbar
</div>
{{/this}}
However, if run the 'template()' function with no params, nothing outputs. Yet, if I pass something in like: "template('ben')", it outputs the static HTML fine. Anyone got any ideas?
Does template() always have to have something passed into it to render the template?
EDIT:
If I remove the {{#this}} from the template, then it works with no parameters...
The this at the top level of a template is the argument you supply to the compiled template function. So, given this:
var o = { ... };
var t = Handlebars.compile(some_template_text);
t(o);
this will be o at the top level of the template. So, if you say template(), this is undefined inside the template and {{#this}} won't do anything because undefined is false in a boolean context.
You can see this clearly if you use this template:
<script id="tmpl-nav-account" type="text/x-handlebars-template">
{{#this}}
<div class="nav-account">
{{this.where_is}}
</div>
{{/this}}
</script>​
and this JavaScript:
var t = Handlebars.compile($('#tmpl-nav-account').html());
console.log(t());
console.log(t({ where_is: 'pancakes house?' }));​
Demo: http://jsfiddle.net/ambiguous/fS8c9/

How to refer to Object of current iteration in Handlebars

Is there any way to get the object of the current iteration in Handlebars?
code:
<script id="HandleBarTemplate1" type="text/x-handlebars-template">
{{#each objArr}}
<img src="{{objField1}}"/>
<strong>Name:</strong> {{objField2}}
<input type="button" onclick="processObject({{.}});"/>
{{/each}}
</script>
I've mentioned processObject({{.}}) That is incorrect. That's where I need a replacement/Solution.Hope you get what I'm tryin' to say.
The contents of objArr might look like
var objArr = [{objField1:"smith.jpg",objField2:"Smith"},{objField1:"jane.jpg",objField2:"Jane"},...]
Template compilation code is:
var source = document.getElementById("HandleBarTemplate1").innerHTML;
var compiledTemplate = Handlebars.compile(source);
var html = compiledTemplate({objArr:objArr});
If I could get the reference to the Object, then It's so easy to process the data. Rather than passing a field to the function and searching through entire array to get the required object and then process it.
I Prefer a solution without a custom block helper/custom expression helper but if none exists, I'd rather go for a custom block helper. Any solution without searching through the entire array is welcome!
I would suggest a different route.
Knowing that you already have a reference to objArr, make a global or name-spaced variable pointing to it.
For example: window.objArr = objArr;
Create your click handler that does whatever you wish:
function processObject(key){
}
call that with your key inside your template:
< script id="HandleBarTemplate1" type="text/x-handlebars-template">
{{#each objArr}}
<img src="{{objField1}}"/>
<strong>Name:</strong> {{objField2}}
<input type="button" onclick="processObject({{objField2}});"/>
{{/each}}
</script>
Other alternatives:
Customer Handler.
Other alternatives:
If you can't create a reference to objArray, you might could store the properties of the object within data- attributes if you're using html5. processObject could then retrieve them.
I do this with a Handlebars helper and an array of context objects.
Somewhere we have an array of context objects, which will store a reference to each context object we need to reference:
var ContextObjects = [];
We need to register a Handlebars helper function that stores the current context object in the array for us when we place "{{obj}}" in the template. The helper returns the index of the object in the array, which is what gets rendered:
// Must be function and not lambda to preserve the correct 'this'
Handlebars.registerHelper("obj", function ()
{
var contextObject = this; // the current context object
var index = ContextObjects.indexOf(contextObject);
if (index < 0)
{
index = ContextObjects.length;
ContextObjects[index] = contextObject;
}
return index;
});
Next we need a function to retrieve the object from the index on the element:
// Get the context object for the current event (e.g. click or context).
function GetContextObject(event)
{
var $e = $(event.currentTarget);
var index = $e.attr("data-obj");
return ContextObjects[index];
}
We tie them together in the template something like this:
{{#each Items}}
<div data-obj="{{obj}}" onclick="DoSomething(GetContextObject(event));">...</div>
{{/each}}
Which may render something like this:
<div data-obj="0" onclick="DoSomething(GetContextObject(event));">...</div>
<div data-obj="1" onclick="DoSomething(GetContextObject(event));">...</div>
<div data-obj="2" onclick="DoSomething(GetContextObject(event));">...</div>
...

Resources