Handlebars: pass increment to partial for all elements on same level? - handlebars.js

I'm trying to render a nested ul structure using a recursive strategy. I'm facing the problem of passing and increment value to the partial so that I can set a unique class name for each level (which have multiple elements).
#index will not work as it will give each item a different value on the same level. I need all elements on the same level to have the same value, only incremented on each nesting, as in:
pseudo-code
level_element class="indent_1"
level_element class="indent_1"
level_element class="indent_1"
level_element class="indent_1"
level_element class="indent_2"
level_element class="indent_2"
level_element class="indent_2"
level_element class="indent_2"
level_element class="indent_3"
level_element class="indent_3"
level_element class="indent_3"
level_element class="indent_3"
etc.
I'm doing this (well trying to) pseudo-code
:
{{#each this}}
element_with_class_indent_{{INITIAL_INCREMENT_VALUE}}
{{#if sub_level}}
{{> level indent=INITIAL_INCREMENT_VALUE + 1}} // Pass value to all elements on next level
{{/if}}
{{/each}}
Hope this makes sense.

I would do this by creating a helper to increment an integer. I would design my partial view so that it could recursively call itself, passing the indent as a parameter. I would use a subexpression to increment the value of indent (using my helper) at each nested level.
The increment helper would be very simple:
Handlebars.registerHelper('increment', function (value) {
return value + 1;
});
Assuming a recursive data structure with an array of Items, each with a title and an array of Items, children, the partial view, "menu", would look something like the following:
<ul>
{{#each items}}
<li class="indent_{{../indent}}">
{{title}}
{{#if children}}
{{> menu items=children indent=(increment ../indent)}}
{{/if}}
</li>
{{/each}}
</ul>
To render the menu, the template would call the partial, passing 1 as the initial indent value:
{{> menu items=this indent=1}}
I have created an example fiddle that you can see here.

Related

passing a variable inside helper funtion

I am using a helper inside another helper. I am trying to pass a value ‘post_id’, that i am getting dynamically from ‘show_post’ helper.I want to pass it and then use it inside the query that is returning a set of result back to helper. for some reason , app is crashing. Can someone guide me through this.
{{#each show_post}}
{{posttext}}
{{postedBy}}
{{#each show_comment({{post_id}}) }}
//<--- i am passing the value to helper like this.
<span> <p>{{postedBy}}: <h5>{{commenttext}}</h5>{{createdAt}}</p></span>
{{/each}}
{{/each}}
Template.display_block.helpers({
show_post(){
return PostComment.find({parent: 0},{sort: {createdAt: -1}});
}
});
Template.display_block.helpers({
show_comment(e){
var t1 = e;
var now = PostComment.find({post_id:{$regex:new RegExp(’^’ + t1)}});
return now;
}
});
The first helper generates a array(Mongo Db result).I want to use this array’s elements(that are dynamic) in next helper. So i want to use a variable that can hold the value of array element of first helper and then pass it to next helper. In second helper, i want to pass this variable and use this variable to sort a result in Mongo query. I am new to this so i am unable to understand how this instance
Firstly you don't need to wrap helper arguments in double curly braces or parens.
{{#each show_comment post_id}}
Will do what you need.
You're also making life a bit more complicated for yourself than necessary. You can use the current data context through this in your code. Also unclear why you're using a regex unless you're concatenating something to the post _id.
This allows you to simplify down to:
html:
{{#each show_post}}
{{posttext}}
{{postedBy}}
{{#each show_comment}}
<span><p>{{postedBy}}: <h5>{{commenttext}}</h5>{{createdAt}}</p></span>
{{/each}}
{{/each}}
js:
Template.display_block.helpers({
show_comment(){
return PostComment.find({post_id: this._id);
}
});

handlebars - using #if helpers with two conditions

In my js file, I pass a JSON object with two keys - {'tests': tests, 'isComplete': isComplete}
In my handlebars file, I want to display the tests object based on each test status. At the same time, there's another condition for which I added a helper named 'isRequired' that I want to check the test against only if isComplete is not true.
{{#if isComplete}}
{{#each tests}}
{{#isRequired this}}
// display data
{{/isRequired}}
{{/each}}
{{else}}
{{#each tests}}
// display data
{{/each}}{{/if}}
This code has the duplicate code to display data. I'm still learning handlebars and not sure how to eliminate this redundant code block. Can you help me how I can refactor this? Thank you!
I would probably solve this by adding an isComplete parameter to your isRequired helper. You have not provided the code for your helper, but I imagine it would end up looking something like this:
Handlebars.registerHelper('isRequired', function (context, isComplete, options) {
if (!isComplete) { return options.fn(context); }
/* rest of isRequired implementation here */
});
Your template would then be updated as follows:
{{#each tests}}
{{#isRequired this ../isComplete}}
// display data
{{/isRequired}}
{{/each}}
Note that this implementation applies the isRequired logic only if isComplete is true. This is the same rules as exist in your template code sample. However, these rules appear to contradict the text of your question, which specifies that isRequired should be applied "only if isComplete is not true". If these are the requirements we must meet, we need only remove the not operator from our new guard clause:
if (isComplete) { return options.fn(context); }

How to modify object property before insertion into Blaze template

Let's say I have the following Blaze template helper that fetches some objects from a collection:
PackageInCart: function() {
PackageIds = Carts.findOne({SessionId: SessionID}).Packages;
var PackageObjects = Packages.find({ _id: { $in : PackageIds } } ).fetch();
return PackageObjects;
},
The PackageObjects variable contains objects that have a 'priceperday' property with a certain price value. In the Blaze template, I can easily print this value using:
{{#each PackageInCart}}
<div class="price">{{priceperday}}</div>
{{/each}}
However, what if I want to modify the 'priceperday' value from the Helper function before it gets printed in the template? What would be the correct way to do this?
One solution that came to mind was to make a for loop that iterates over the objects and does something like Object.defineProperty() to change the priceperday property into the new value.
I want to know if there's an easier or quicker way using Blaze methods to modify the object property that gets printed with the curly braces.
If you want to do this using blaze you could do this using another helper.
weeklyPrice: function(priceperday){
return priceperday * 7;
}
Which would be called like this
{{#each PackageInCart}}
<div class="price">{{weeklyPrice priceperday}}</div>
{{/each}}
More info about spacebars helper arguments in the docs

Using a helper with arguments AS a helper argument in Spacebars

So I am trying use a helper as an argument of another helper in Spacebars. In the example below, 'getResultInfo' is a helper that gets data specific to the arguments passed, and 'formatResult' is a helper that formats its result and the results of other helpers.
<template name="example">
{{#each collectionResults}}
Label: {{formatResult getResultInfo this._id 'text'}}
{{/each}}
</template>
The issue I'm having is that Spacebars thinks that the arguments for 'getResultInfo' are the just second and third arguments for 'formatResult'. I'd really like to keep the helper's functions separate (ie. not having to format the result at the end of the 'getResultInfo' and every other helper that I have). Is there any alternate syntax or method of doing what I'm trying to achieve?
I think that you cannot chain two helpers with parameters on the second one like you did. Subsequent parameters will still be interpreted as parameters from the first helper.
I see two ways for solving this problem:
1) you could get rid of the parameters and use the data context provided by each.
each bind the data context to this so in getResultInfo you could just use this._id directly. But you have to remember that you need this data context each time you use this helper. And this pose a problem with the 'text' parameter which does not depend from the data context.
2) you could create a function corresponding to your getResultInfo helper and use it directly in the formatResult helper like this:
getResultHelper = function(id, text) {
//do what you want
};
//bind the function to your getResultInfo (example with a global helper)
Template.registerHelper('getResultInfo', getResultHelper);
//use the function in your template helper
Template.example.helpers({
formatResult: function(format) {
return getResultHelper(this._id, format);
}
});
//and finally your template
<template name="example">
{{#each collectionResults}}
Label: {{formatResult 'text'}}
{{/each}}
</template>
FYI, this is now possible in Meteor 1.2, via Spacebars sub expressions in Blaze.
<template name="example">
{{#each collectionResults}}
Label: {{formatResult (getResultInfo this._id 'text')}}
{{/each}}
</template>

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}}

Resources