Meteor: Build one new object with strings out of four results - javascript

I have filtered results from four different publications (with different structure).
Router.route('/anything/:_id', {
name: 'anything',
data: function () {
return {
result1: Collection1.find({'article.reference': this.params._id}),
result2: Collection2.find({'edition.section.reference': this.params._id}),
result3: Collection3.find({'reference': this.params._id}),
result4: Collection4.find({'anything.reference': this.params._id})
};
}
});
Right now I just display them like this:
<h4>Result 1</h4>
{{#each result1}}
{{#each article}}
{{author}}. {{title}}. {{../journal}} ({{year}}):{{edition}}; S.{{pageNumbers}}
{{/each}}
{{/each}}
[...]
<h4>Result 4</h4>
{{#each result4}}
{{#each edition}}
{{#each section}}
{{../../author}}. {{../../book}} ({{../year}}). {{../edition}}. {{../../publisher}}. S.{{pageNumbers}}
{{/each}}
{{/each}}
{{/each}}
I do this for every single result given in the router-data. So I get four sorted lists.
But I need just one big list with all elements in it sorted in general. Therefore I would like to build the string first (right now this is be done in the template) for every result (each Collection will be treated different as the result-string differs) and then sort the resulted array to push this in the template.
So the template would just be:
<h4>Result</h4>
{{#each result}}
<p>{{line}}</p>
{{/each}}

Wouldn't this be a choice?
var cursor = Collection.find();
cursor.forEach(function(doc){
console.log(doc._id);
// fill new object here...
});

Related

Ember Handlebars nested each not working

The following Ember Handlebars template renders the 1st row, but does not render the one inside the nested each (or inner each)
<table width="50%">
{{#each someData.items as |item|}}
<tr> <!-- This one RENDERS -->
<td width="25%"><span class="boldTxt">{{item.fld1}}</span></td>
<td width="25%">{{item.fld2}}</td>
</tr>
{{#each item.Reas as |rea|}}
<tr> <!-- This one does not RENDER -->
<td>{{rea}}</td>
</tr>
{{/each}}
{{/each}}
</table>
What is the issue??
I am using Ember version 1.13
Most likely, your problem is that you are using Ember2.0 or above (based on your outer each loop) so your inner each loop has a now invalid (formerly deprecated) format. Also, you are using the same variable name item for both loops, which won't work properly.
http://guides.emberjs.com/v2.1.0/templates/displaying-a-list-of-items/
Just use the same format as in the outer loop:
Change:
{{#each item in item.Reasons}}
To:
{{#each item.Reasons as |reason|}}
EDIT
If your Reas arrays look as you've described in the comments:
item.Reas = [null]; // arrays containing a single `null` value
Then handlebars will show an empty string for these values since Handlebars coerces undefined and null to an empty string.
{{reas}} {{!-- if reas is null then an empty string is printed --}
If you want to show null and undefined values, you can make a simple helper to do so:
// helpers/show-value.js
import Ember from "ember";
export default Ember.Helper.helper(function(params) {
let value = params[0];
if(value === undefined) { return 'undefined'; }
if(value === null) { return 'null'; }
return value;
});
EDIT 2
Based on your explanation in the comment:
Since you are using Ember 1.13, you need a work around to achieve this. Here is one way:
// components/each-keys.js
export default Ember.Component.extend({
object: null, // passed in object
items: Ember.computed('object', function() {
var object = Ember.get(this, 'object');
var keys = Ember.keys(object);
return keys.map(function(key) {
return { key: key, value: object[key]};
})
})
})
Usage:
{{#each-keys object=item.Reas as |key value|}}
<tr>
<td>{{key}}</td>
<td>{{value}}</td>
</tr>
{{/each-keys}}
Here is a running example
If you update to Ember 2.0, which should be pretty straightforward from 1.13 (since 2.0 is basically 1.13 without deprecations) you can use the each-in helper to iterate over an object and get access to both its keys and values. Here is a simple example:
{{#each-in items as |key value|}}
<p>{{key}}: {{value}}</p>
{{/each-in}}

Handlebars Helper to Compare Values (if v1 === v2), and Render Upper-Level Scope?

For the actual call, I need something like this:
<script id="messagesTemplate" type="text/x-handlebars-template">
{{#each messages.messages}}
{{#each to}}
{{#ifCond username messages.sessionUserName}}
<h1>{{username}} is equal to {{messages.sessionUserName}}</h1>
{{else}}
<h1>{{username}} is not equal to {{messages.sessionUserName}}</h1>
{{/ifCond}}
{{/each}}
{{/each}}
Where, in the db, 'to' is an array of docs that each have a 'username'..that thus need === the messages.sessionUserName to then template/render HTML for certain values (e.g. {{#if read.marked}} )
"to" : [
{
"user" : ObjectId("53aada6f8b10eb0000ec8a90"),
"username" : "username1",
"updated" : ISODate("2014-07-01T19:39:45Z"),
"_id" : ObjectId("53b30e81b0eff5cb1e2ecb21"),
"read" : {
"marked" : true
}
}
]
Worth noting, both usernameTest & sessionUserName are values appended to the end of the res.json() via express, so they are accessible by messages.usernameTest & messages.sessionUserName, but they are not present in each document..these values are only available in the global parent doc.
res.json({
messages : messages,
sessionUserName: req.session.username,
usernameTest: usernameTest
});
This factor may be responsible for why each of these only render is equal to, but doesn't really make sense for the third (The ../ path segment references the parent template scope):
{{#each messages.messages}}
<h1>{{usernameTest}} is equal to {{sessionUserName}}</h1>
<h1>{{../usernameTest}} is equal to {{../sessionUserName}}</h1>
<h1>{{../messages.usernameTest}} is equal to {{../messages.sessionUserName}}</h1>
Drawing from https://stackoverflow.com/a/9405113/3095287 for the custom comparison helper, the template that follows {{#ifCond v1 v2}} doesn't seem to render upper-level scoped elements..
Handlebars.registerHelper('ifCond', function(v1, v2, options) {
if(v1 === v2) {
return options.fn(this);
}
return options.inverse(this);
});
The ifCond comparison does work outside of an {{#each}} block:
<script id="messagesTemplate" type="text/x-handlebars-template">
{{#ifCond messages.usernameTest messages.sessionUserName}}
<h1>{{messages.usernameTest}} is equal to {{messages.sessionUserName}}</h1>
{{else}}
<h1>{{messages.usernameTest}} is not equal to {{messages.sessionUserName}}</h1>
{{/ifCond}}
{{#each messages.messages}}
..
..as that renders:
username1 is equal to username1
However, it does not work inside of an {{#each}} block:
{{#each messages.messages}}
{{#ifCond messages.usernameTest messages.sessionUserName}}
<h1>{{messages.usernameTest}} is equal to {{messages.sessionUserName}}</h1>
{{else}}
<h1>{{messages.usernameTest}} is not equal to {{messages.sessionUserName}}</h1>
{{/ifCond}}
...
..as it only renders:
is equal to
Even with {{../element}}
{{#each messages.messages}}
{{#ifCond messages.usernameTest messages.sessionUserName}}
<h1>{{../messages.usernameTest}} is equal to {{../messages.sessionUserName}}</h1>
{{else}}
<h1>{{../messages.usernameTest}} is not equal to {{../messages.sessionUserName}}</h1>
{{/ifCond}}
...
..the rendering is:
is equal to
Ok so the main thing is you want to be able to gain access to your top level scope deeper down. Ive done this using a customer helper that adds a little extra to the normal each block.
so here is the normal handle bars each
Handlebars.registerHelper('each', function(context, options) {
var ret = "";
for(var i=0, j=context.length; i<j; i++) {
ret = ret + options.fn(context[i]);
}
return ret;
});
all i do is set 'this' to a property called root and pass it back with the result. To overcome nested loops i check for the existence of my 'root' property and if it exists I pass it along other wise root = this.
Handlebars.registerHelper("myEach", function(context, options) {
var ret = "";
for (var i = 0, j = context.length; i < j; i++) {
if (this.root) {
root = this.root;
} else {
root = this;
}
ret = ret + options.fn(_.extend({}, context[i], {
root: root
}));
}
return ret;
});
Now no matter how deep i am in my loops if i want to use something from the root i just use root.property.
A working codepen can be found here with a simplified version of your example.
EDIT: Ok so 5 minutes later after posting this i read about paths in another templating language and then realise handlebars also has paths. so you don't need to do the above you can just use the relative nested path in your template like below. I'm going to continue using the helper though as I think it is tidier to go root.property rather than adding n many "../" for how nested your are.
here is a working example using the paths
<script type="text/x-handlebars-template" id="messages-template">
Logged in user {{userSession}}
{{#each messages}}
<ul>
<li> Title: {{title}}</li>
<li> Conetent: {{content}}</li>
<li> TO:
<ul>
{{#each to}}
<li>{{user}} {{#ifvalue user ../../userSession}}
thats me
{{else}}
thats not me
{{/ifvalue}}</li>
{{/each}}
</ul>
</li>
</ul>
{{/each}}
</script>

Counter for handlebars #each

In Handlebars, say I have a collection of names. How can I do
{{#each names}}
{{position}}
{{name}}
{{/each}}
where {{position}} is 1 for the first name, 2 for the second name, etc.? Do I absolutely have to store the position as a key in the collection?
You can do this with the built-in Handlebars #index notation:
{{#each array}}
{{#index}}: {{this}}
{{/each}}
#index will give the (zero-based) index of each item in the given array.
Please note for people using Handlebars with the Razor view engine you must use the notation ##index to avoid compilation errors.
For more built-in helpers see http://handlebarsjs.com/
Handlebars.registerHelper("counter", function (index){
return index + 1;
});
Usage:
{{#each names}}
{{counter #index}}
{{name}}
{{/each}}
While you can't do this with any native Handlebars helper, you can create your own. You can call Handlebars.registerHelper(), passing it a string with the name you want to match (position), and a function that would return the current position count. You can keep track of the position number in the closure where you call registerHelper. Here is an example of how you can register a helper called position that should work with your template example.
JavaScript:
// Using a self-invoking function just to illustrate the closure
(function() {
// Start at 1, name this unique to anything in this closure
var positionCounter = 1;
Handlebars.registerHelper('position', function() {
return positionCounter++;
});
// Compile/render your template here
// It will use the helper whenever it seems position
})();
Here is a jsFiddle to demonstrate: http://jsfiddle.net/willslab/T5uKW/1/
While helpers are documented on handlebarsjs.com, this took some effort for me to figure out how to use them. Thanks for the challenge, and I hope that helps!
only you have to use {{#index}}
example:
{{#.}}
<li class="order{{#index}}"> counter: {{#index}}</li>
{{/.}}
Here is my preferred solution. Register a helper that extends the context to include your position property automatically. Then just use your new block helper (ex. #iter) instead of #each.
Handlebars.registerHelper('iter', function (context, options) {
var ret = "";
for (var i = 0, j = context.length; i < j; i++) {
ret += options.fn($.extend(context[i], {position: i + 1}));
}
return ret;
});
Usage:
{{#iter names}}
{{position}}
{{name}}
{{/iter}}
adapted from http://rockycode.com/blog/handlebars-loop-index/
you can get value just from index inside the list.
{{#each list}}
#index
{{/each}}
Current method,
Since Handlebars API V2 they already include an #number
It's basically an index of the iterator start with 1.
So, This is what you could have done.
{{#foreach names}}
{{#number}}
{{name}}
{{/foreach}}
Reference: https://ghost.org/docs/api/v3/handlebars-themes/helpers/foreach/
This works for me
{{#each posts}}
<tr>
<td>{{#index}} </td>
<td>{{name}}</td>
</tr>
{{/each}}

Handlebars.js: How to access parent index in nested each?

How to access parent #index value in each-loop?
Tried the following:
{{#each company}}
{{#each employee}}
{{../#index}} // how to access company index here?
{{/each}}
{{/each}}
This results to an error:
Expecting 'ID', got 'DATA'
There is a syntax error in the example. The correct syntax is {{#../index}}.
We are looking at ways that we can support custom naming of these parameters in future versions of the language so this is easier to deal with.
https://github.com/wycats/handlebars.js/issues/907
This worked for me:
{{#each company}}
{{setIndex #index}}
{{#each employee}}
{{../index}}
{{/each}}
{{/each}}
JS:
Handlebars.registerHelper('setIndex', function(value){
this.index = Number(value + 1); //I needed human readable index, not zero based
});
Just make sure the company object doesn't have index property.
Answer: {{#../index}}
From the Handlebars docs (see bottom of 'each' helper section):
"Nested each blocks may access the interation variables via depted paths. To access the parent index, for example, {{#../index}} can be used."
NOTE: I'm using v1.3 so it's at least that old.
REMINDER: Helpers are your last best option. 9/10 there is a better solution.
It looks like there's a new syntax in Ember v2.2.0. I tried all the answers here and they didn't work for me.
What I found worked was naming the parent loop's index and the child loop's index.
{{#each parents as |parent parentIndex|}}
{{parentIndex}}
{{#each children as |child childIndex|}}
{{parentIndex}}
{{childIndex}}
{{/each}}
{{/each}}
registe an Helper method:
Handlebars.registerHelper('eachWithIndex', function(cursor, options) {
var fn = options.fn, inverse = options.inverse;
var ret = "";
var idx = -1;
//console.log(cursor);
cursor.forEach(function(item){
idx++;
console.log(item.index);
item.index = idx;
ret+=fn(item);
});
return ret;
});
handlebars template:
{{#eachWithIndex outer}}
{{#each inner}}
{{../index}} // access outer index like this. I used hanlebars V1.3.0
{{index}} // access inner index
{{/each}}
{{/eachWithIndex}}

Handlebars template for Ember.js objects grouped by two

I have my data in the collection and I need to render them into the template. How can I achieve the result when the data is grouped by two? I need to sort them like this:
._______________________
| 1 | 3 | 5
|___|___|_______________
| 2 | 4 | and so on...
|___|___|_______________
I have created vertical div element for each 1+2, 3+4, ... pair to style the items like this. How can I render the data in to such grid with handlebars? All I can do is this:
{{#each App.myController}}
... render item ...
{{/each}}
Firstly, i'd attempt to do this in CSS if at all possible in your layout.
If not, your best bet would to add a computed property to your controller that groups them into pairs and do it that way. Something like this:
{{#each App.myController.pairedContent}}
<!-- view for content.firstObject -->
<!-- view for content.lastObject -->
{{/each}}
App.MyController = Ember.ArrayController.extend({
pairedContent: ( function(){
return this.get('content').reduce( function(array, object, index){
if(index % 2){
array.get('lastObject').pushObject(object)
}
else{
array.pushObject(Ember.A([object]))
}
return array
}, Ember.A())
}).property('content')
})

Categories