Access to next array item in handlebars.js - node.js

I need access to next item in array while using foreach loop in handlebars.js.I've tried:
{{#each items}}
{{#ifEven #index}}
<p>{{this.description}} - {{../items.[#index+1].description}}</p>
{{/ifEven}}
{{/each}}
but it seems that's not working correctly.

I already solved it using helper:
hbs.registerHelper('nextItem', function (array, index, options) {
return options.fn(array[index + 1]);
});
And .hbs template now looks like this:
{{#each items}}
{{#ifEven #index}}
<p>{{this.description}} - {{#nextItem ../items index}} {{description}} {{/nextItem}}</p>
{{/ifEven}}
{{/each}}
I know, that last element will not show, but this doesn't matter in my case.

Related

Handlebars, nested each with parallel arrays

I have two arrays like below.
const arr1 = [ 1, 2 ]
const arr2 = [[1,3,5],[2,4,6]]
I want loop through arr1 and inside loop I want to loop through arr2[arr1.index].
My attempt is below, but of course it is not working:
{{#each arr1}}
{{#each arr2.[ #index ]}} //index is from #each arr1
{{/each}}
{{/each}}
Any ideas?
You can achieve that result by doing:
{{#each arr2}}
{{#each this}}
{{ this }}
{{/each}}
{{/each}}
Well answer is lookup helper and ../ for reaching parent scope.
{{#each arr1}}
{{#each (lookup ../arr2 #index)}} //index is from #each arr1
{{/each}}
{{/each}}

Check two strings with HBS (express-handlebars)

I'm trying to compare two different Strings by HBS.
The function look like this:
hbs.registerHelper('if_equal', function(a, b, opts) {
if (a == b) {
return opts.fn(this)
} else {
return opts.inverse(this)
}
});
Works great!
But the problem is in this lines:
{{#each info.categories}}
<li>{{this.title}}</li>
{{!-- {{#if_equal this.title "מלגזות"}}
{{/if_equal}} --}}
{{/each}}
s you can see I'm trying check if two string are equal inside the loop.
The problem is how I can back to {{info}} one.
To the global object inside the loop.
And than make loop into the object.
I'm not sure I understand your question, but if you want to access info in the loop you can do it by #root.info
https://handlebarsjs.com/reference.html#data-root

Handlebars disable attribute if id exist

I have list of electoralUnits and I need to disable some in that list if that id of electoralUnit is added in other collection called StateResult.
Route:
router.get('/', (req, res) => {
StateResult.find({})
.then(stateResults => {
ElectoralUnit.find({})
.then(electoralUnits => {
StateList.find({})
.then(stateLists => {
res.render('home/results/stateResults', {
stateResults: stateResults,
electoralUnits: electoralUnits,
stateLists: stateLists
});
});
});
});
});
Now, I tried this in handlebars with if helper:
<select name="electoralUnit" multiple class="form-control" id="exampleFormControlSelect2" size="40">
{{#each electoralUnits}}
<option value="{{id}}" {{#each stateResults}} {{#if this}} disabled {{/if}} {{/each}}>{{name}}</option>
{{/each}}
</select>
and a lot of variations of this, like:
<option value="{{id}}" {{#each stateResults}} {{#if electoralUnit}} disabled {{/if}} {{/each}}>{{name}}</option>
but nothing works. Where am I wrong?
This is data from mongodb:
electoralunits collection
{"_id":"5ab906612f30fe23dc592591","town":"5ab903952e9dc70408a81e32","name":"1. МЗ Аеродром - Дом Здравља","__v":0,"electoralNumber":4200,"safeVoter":360,"date":"2018-04-25T15:19:37.900Z"}
stateresults collection
{"_id":"5ac4e01d46fa2b21280bd981","electoralUnit":"5ab906612f30fe23dc592591","allVotes":100,"validVotes":90,"invalidVotes":10,"partyVotes":[50,10,10,10,5,5],"__v":0}
I tried as #doowb explain me with custom handlebars helper:
includes: function(arr, prop, val, options) {
const matches = arr.map(item => item[prop]).includes(val);
if (matches) {
return options.fn(this);
} else {
return options.inverse(this);
}
}
<select name="electoralUnit" multiple class="form-control" id="exampleFormControlSelect2" size="40">
{{#each electoralUnits}}
<option value="{{this._id}}" {{#includes ../stateResults "electoralUnit" this._id}} hidden {{/includes}}>{{name}}</option>
{{/each}}
</select>
but this won't work either.
It would help if you show that the stateResults, electoralUnits, and stateLists objects look like, but I think your issue is with knowing which "depth" you're currently trying to access data from. In the express middleware you're passing all of those objects to the template at the root of the object:
{
stateResults: [], // assuming this is an array
electoralUnits: [], // assuming this is an array
stateLists: [] // assuming this is an array
}
In the {{#each stateResults}} you'll need to use the parent specifier: {{#each ../stateResults}} since the first {{#each}} block created a new depth. Also, the logic of the data objects doesn't seem correct since you don't need to add multiple disabled attributes to the option tag. If you post what the actual data objects look like, I'll edit this answer with additional information.
Edit: After getting more information, I think that using the {{pluck}} and {{inArray}} helpers and subexpressions like the following will achieve your goal. If the ../stateResults does work, then try #root.stateResults (I'm still assuming that the collections returned are arrays of those mongodb objects):
<select name="electoralUnit" multiple class="form-control" id="exampleFormControlSelect2" size="40">
{{#each electoralUnits}}
<option value="{{this._id}}" {{#inArray (pluck ../stateResults "electoralUnit") this._id}} disabled {{/inArray}}>{{name}}</option>
{{/each}}
</select>
If you don't want to use the handlebars-helpers library (indicated in a comment below) you can create your own helper:
Handlebars.registerHelper('includes', function(arr, prop, val, options) {
const matches = arr.map(item => item[prop]).includes(val);
if (matches) {
return options.fn(this);
} else {
return options.inverse(this);
});
Then you can use that helper like this:
<select name="electoralUnit" multiple class="form-control" id="exampleFormControlSelect2" size="40">
{{#each electoralUnits}}
<option value="{{this._id}}" {{#includes ../stateResults "electoralUnit" this._id}} disabled {{/includes}}>{{name}}</option>
{{/each}}
</select>

Compare iteration values using handlebars

I want to compare iteration values using handlebars. This is my code
{{#each accounts}}
{{#each projects}}
{{#if}} (compare accounts.project_id with projects._id)
// display the project name
{{else}}
// display not found
{{/if}}
{{/each}}
{{/each}}
Please help. I'm new to handlebars/
Use the {{compare}} helper from the handlebars-helpers module.
{{#each accounts}}
{{#each projects}}
{{#compare accounts.project_id "==" projects._id)
// display the project name
{{else}}
// display not found
{{/compare}}
{{/each}}
{{/each}}
Refer to the documentation on how to install and use the helpers.
You can do so with a simple helpers in Handlebars like so:
Handlebars.registerHelper('if_eq', function(a, b, opts) {
if(a == b)
return opts.fn(this);
else
return opts.inverse(this);
});
and in your code...
{{#each accounts}}
{{#each projects}}
{{#if_eq accounts.project_id projects._id}}
// display the project name
{{else}}
// display not found
{{/if_eq}}
{{/each}}
{{/each}}

Strange results with nested handlebar blocks in Meteor

I am trying to set default value in an html select, but without sucess.
I'm doing the initial populating like this:
<template name="demo">
<select>
{{#each foo}}
<option>{{this}}</option>
{{/each}}
</select>
</template>
And i set the possible options in the model like this:
Template.demo.foo = ["aaa","bbb","ccc"];
So far, everything work as intended.
Now i'm trying to display row of the collection collec, populating the select with the defaut recorded foo value (aaa or bbb or ccc).
My understanding is that you must add "selected" to the tag.
So i do something like this with multiple nested blocks:
<template name="demo">
{{#each collecs}}
{{_id}}
<select>
{{#each foos}}
<option{{#if isSelected this ../this}}selected{{/if}}>{{this}}</option>
{{/each}}
</select>
{{/each}}
</template>
And on the model front:
Template.demo.foos = ["aaa","bbb","ccc"];
Template.demo.collecs = function(){
return Collec.find({});
};
Template.demo.isSelected = function(fooToCheck, record){
var rid= record._id;
var currentRecord = Collec.findOne({_id:rid});
return (fooToCheck==currentRecord.foo);
};
The problem is that it does not work.
The dropdown stays empty, and the generated html code show something like this:
" >aaa "
I have checked in the js part, all seems to work correctly, true/false are adequately returned.
Thank in advance for your help.
Handlebars conditionals don't do well inline. In fact, I'm not sure if they work inline anywhere. What was happening was that the Handlebars templating engine didn't understand your html, so it skipped over it, which is why you saw '>aaa'.
The following code works. I took the liberty of simplifying your isSelected function.
Template:
<template name="demo">
{{#each collecs}}
{{_id}}
<select>
{{#each foos}}
{{#if isSelected this ../foo}}
<option selected>{{this}}</option>
{{else}}
<option>{{this}}</option>
{{/if}}
{{/each}}
</select>
{{/each}}
</template>
JavaScript:
Template.demo.foos = ["aaa","bbb","ccc"];
Template.demo.collecs = function(){
return Collec.find({});
};
Template.demo.isSelected = function(fooToCheck, recordFoo){
return (fooToCheck === recordFoo);
};

Resources