For loop in Swig template engine - node.js

I'm using Swig as a template engine for Express.js and I found no way to make a for loop with a variable like so:
for(var i=0; i<100; i++){
//whatever
}
Is this even possible?

As posted on your github issue for the same question, loops like this don't exist in Swig. You can iterate over actual objects and arrays, however. (See for-tag documentation).
Otherwise, you could create a range helper, as discussed here
swig.setDefaults({ locals: {
range: function (start, len) {
return (new Array(len)).join().split(',').map(function (n, idx) { return idx + start; });
}
}});

Related

Magento - How to pass value from template to js file?

I developed an extension where you can define a connection id for newsletter 2 go.
I need to pass this id to the script skin\frontend\base\default\fekete\Newsletter2Go\js\utils.js
Template:
My first approach was to pass it to the script by adding a get parameter to it.
{... Hint: I removed the rest code which is not relevant for this question ... }
(window,document,"script","skin/frontend/base/default/fekete/Newsletter2Go/js/utils.js?id=<?php echo $id ?>","n2g");
I checked if this worked like this:
alert(findGetParameter("id"));
function findGetParameter(parameterName) {
var result = null,
tmp = [];
var items = location.search.substr(1).split("&");
for (var index = 0; index < items.length; index++) {
tmp = items[index].split("=");
if (tmp[0] === parameterName) result = decodeURIComponent(tmp[1]);
}
return result;
}
But it alerts null.
How can I pass the id to the script, without having to move the whole script to the template?
I solved it by creating a new extension. I developed a controller and then just made a XHR Request to my route, which just returns the needed value.

Sequelize.js afterFind argument explanation

I'm trying to implement an afterFind hook on a model and can't quite figure out what the semantics are. I'll pulled the following together from trial and error using the doc and other StackOverflow questions as guidelines.
My goal is to massage the result (by applying get(plain : true)) and pass the transformed value as the result of the promise. For instance, I'd expect/want this to return an empty result set:
hooks: {
afterFind: function(result, options, fn)
{
result = [];
}
},
but it just causes the request to hang. Documentation says the arguments are pass by reference and doesn't include a return. Other samples imply something like:
hooks: {
afterFind: function(result, options, fn)
{
result = [];
return fn(null, result);
}
},
which doesn't hang, but doesn't change my result set. Not to mention, I've no idea what the magical "fn" argument is/does.
I had a similar problem. This is because when you do a findAll the argument passed to the hook is an array of values instead of a single object. I did this as a workaround -
hooks: {
afterFind: function(result) {
if(result.constructor === Array) {
var arrayLength = result.length;
for (var i = 0; i < arrayLength; i++) {
result[i].logo = "works";
}
} else {
result.logo = "works";
}
return result;
}
}
In the above code I change the logo attribute of the record(s) after finding it.

NodeJS V8 Pass additional argument to callback

I write an application using NodeJS, ExpressJS, MongoDB (with Mongoose)...
Everything work perfectly, but, when I have a loop for fetch records and do something with the results, like this:
for(var i = 0; i < 10; i++) {
recods.findOne({number: i}, function(err,doc){
...
});
}
The variable "i" in the scope of callback function is passed by reference and the result is not the desired.
When the callback is called the loop has already run and the variable has changed.
If I try to pass argument as anonymous function, does not work, because it replace the needed arguments:
for(var i = 0; i < 10; i++) {
records.findOne({number: i}, (function(err,doc){
...
})(i));
}
In this way, I lost the "err,doc" arguments,
What can I do to solve this big problem?
You can bind it to your callback to create a partial function with its first argument set to i:
for (var i = 0; i < 10; i++) {
records.findOne({number: i}, function(i, err, doc) {
...
}.bind(records, i));
}
You're applying an anonymous function in the wrong place. It should be applied outside of the function that uses i, not to the callback function.
for (var i = 0; i < 10; i++) {
(function(i) {
records.findOne({number: i}, function(err, doc) {
...
});
}(i));
}
While with a few simple fix-ups to capture the value of i in a closure works as shown in the other answers, you might also consider using Mongoose in a different and likely more efficient way:
var numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
records.find( { number : { $in : numbers } }, function(err, allDocs) {
if (err) { throw err; }
// allDocs are now available in an array (they aren't ordered)
// allDocs.length
// allDocs[0].number ...
});
Using the $in operator (reference) makes just one call to the Database, and finds all matching documents, rather than doing individual calls.

mongodb : how to get documents that contains nested field data

suppose my data is
db.posts.save({postid:1,postdata:"hi am ",comments:["nice","whats bro"]});
so in this case how to iterate comments
means
cursor=selct * from posts;
cursor 1=selct * from comments where postid=:cursor.postid
for (i in cursor)
for(j in cursor1)
One way to iterate over the comments would be as follows:
db.posts.find().forEach(
function(doc) {
// iterate over the comments in your preferred javascript way
var i;
for (i=0; i<doc.comments.length; ++i) {
// Do whatever you want with doc.comments[i] here
}
}
)

Map/Reduce differences between Couchbase & CloudAnt

I've been playing around with Couchbase Server and now just tried replicating my local db to Cloudant, but am getting conflicting results for my map/reduce function pair to build a set of unique tags with their associated projects...
// map.js
function(doc) {
if (doc.tags) {
for(var t in doc.tags) {
emit(doc.tags[t], doc._id);
}
}
}
// reduce.js
function(key,values,rereduce) {
if (!rereduce) {
var res=[];
for(var v in values) {
res.push(values[v]);
}
return res;
} else {
return values.length;
}
}
In Cloudbase server this returns JSON like:
{"rows":[
{"key":"3d","value":["project1","project3","project8","project10"]},
{"key":"agents","value":["project2"]},
{"key":"fabrication","value":["project3","project5"]}
]}
That's exactly what I wanted & expected. However, the same query on the Cloudant replica, returns this:
{"rows":[
{"key":"3d","value":4},
{"key":"agents","value":1},
{"key":"fabrication","value":2}
]}
So it somehow only returns the length of the value array... Highly confusing & am grateful for any insights by some M&R ninjas... ;)
It looks like this is exactly the behavior you would expect given your reduce function. The key part is this:
else {
return values.length;
}
In Cloudant, rereduce is always called (since the reduce needs to span over multiple shards.) In this case, rereduce calls values.length, which will only return the length of the array.
I prefer to reduce/re-reduce implicitly rather than depending on the rereduce parameter.
function(doc) { // map
if (doc.tags) {
for(var t in doc.tags) {
emit(doc.tags[t], {id:doc._id, tag:doc.tags[t]});
}
}
}
Then reduce checks whether it is accumulating document ids from the identical tag, or whether it is just counting different tags.
function(keys, vals, rereduce) {
var initial_tag = vals[0].tag;
return vals.reduce(function(state, val) {
if(initial_tag && val.tag === initial_tag) {
// Accumulate ids which produced this tag.
var ids = state.ids;
if(!ids)
ids = [ state.id ]; // Build initial list from the state's id.
return { tag: val.tag,
, ids: ids.concat([val.id])
};
} else {
var state_count = state.ids ? state.ids.length : state;
var val_count = val.ids ? val.ids.length : val;
return state_count + val_count;
}
})
}
(I didn't test this code, but you get the idea. As long as the tag value is the same, it doesn't matter whether it's a reduce or rereduce. Once different tags start reducing together, it detects that because the tag value will change. So at that point just start accumulating.
I have used this trick before, although IMO it's rarely worth it.
Also in your specific case, this is a dangerous reduce function. You are building a wide list to see all the docs that have a tag. CouchDB likes tall lists, not fat lists. If you want to see all the docs that have a tag, you could map them.
for(var a = 0; a < doc.tags.length; a++) {
emit(doc.tags[a], doc._id);
}
Now you can query /db/_design/app/_view/docs_by_tag?key="3d" and you should get
{"total_rows":287,"offset":30,"rows":[
{"id":"project1","key":"3d","value":"project1"}
{"id":"project3","key":"3d","value":"project3"}
{"id":"project8","key":"3d","value":"project8"}
{"id":"project10","key":"3d","value":"project10"}
]}

Resources