Using a helper with arguments AS a helper argument in Spacebars - meteor

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>

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

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

How to pass Meteor Spacebars helper with parameter into another helper attribute?

everybody :) For example I have something like this:
{{> someCoolHelper
someParam1='someVal1'
someParam2='someVal2'
someParam3='SomeVal3'
someParam4=someAnotherHelper 'value param to this helper'
}}
So I have a function, that computes some value depending on parameter that can be passed in it. So how can I do this?
as you may read in a forum thread at meteor forum this is not possible by now in spacebars.
We began with a new package for chaining methods and arguments but its not released yet. What you could do is to use the WITH element like
Instead something like:
{{> someCoolHelper
someParam1='someVal1'
someParam2='someVal2'
someParam3='SomeVal3'
someParam4=someAnotherHelper 'value param to this helper'
}}
can be managed by:
{{#with someAnotherHelper 'value param to this helper' }}
{{> someCoolHelper
someParam1='someVal1'
someParam2='someVal2'
someParam3='SomeVal3'
someParam4=this
}}
{{/with}}
I don't like it, but sometimes necessary
Tom
P.S.: Or drop Spacebars and use React - you won't have such limitations there.
The placeholder solution until template sub expressions are available in Spacebars is to create an helper returning the adequate value in JS :
HTML
{{> myTemplate param1="A" param2=param2}}
JS
function someOtherHelper(param){
console.log(param);
}
Template.registerHelper("someOtherHelper", someOtherHelper);
Template.myTemplate.helpers({
param2: function(){
return someOtherHelper("B");
}
});
You can pass the output of a helper to another helper using parentheses. Pretty sure this is what you're after:
http://blazejs.org/guide/spacebars.html#Calling-helpers-with-arguments
You can also pass the output of a helper to a template inclusion or other helper. To do so, use parentheses to show precedence:
{{> Todos_item (todoArgs todo)}}
Here the todo is passed as argument to the todoArgs helper, then the output is passed into the Todos_item template.
Something I did in a recent project uses this functionality to allow conditional rendering of CSS classes to a component (stripped down for brevity):
# global helper to provide ternary operator
Template.registerHelper('ternary', (condition, resultTrue, resultFalse) => {
return condition ? resultTrue : resultFalse
})
# in my-template.js:
Template.My_template.helpers({
isComplete(score) {
return score === 1
}
})
# in my-template.html:
{{> some_component class=(ternary (isComplete this.score) "green" "red")}}

Write custom iterator for Spacebars

I'm trying to write a custom iterator in spacebars (I'm using meteor 1.1.3). The iterator is to be a sequential for loop (basically to replace my usage of #each when needed since I believe #each is not guaranteed to be sequential in its iteration).
I have tried the following:
In lib -
UI.registerHelper 'sequentialFor', () ->
ret = ""
for i in [0...#.length]
id = #[i]
ret = ret + Template.noop
ret
noop.html -
<template name="noop">
{{> UI.contentBlock this}}
<template>
main.html -
{{#sequentialFor ids}}
<div id="wow-{{this}}">stuff</div>
{{/sequentialFor}}
ids in the above is an array of strings passed from one of main's template helpers.
Right now it complains the the return from my UI helper is [object Object] [object Object].
For sanity's sake I know that if I replace my UI helper with:
UI.registerHelper 'sequentialFor', () ->
//ret = ""
//for i in [0...#.length]
// id = #[i]
// ret = ret + template
id = #[0]
Template.noop
I get that the div in my main.html shows up with the appropriate id as a part of its id attribute as desired. However, I can't seem to make the for loop work.
I can't simply return the div in main.html directly from the helper because I have a lot of divs that I need to wrap with my new iterator, each of which has very different attributes.
I guess the simple question is, how do I define my own block iterator (akin to #each) in spacebars?
The more difficult question may be, what is wrong with my approach above?
I have considered a wide array of resources but have only the found the following to be very helpful:
How to pass an object from to a block helper back to the block in meteor blaze?
https://github.com/meteor/meteor/wiki/Using-Blaze
https://github.com/meteor/meteor/blob/devel/packages/spacebars/README.md
Iterating over basic “for” loop using Handlebars.js
NOTE I'm using coffeescript
I managed to get a custom iterator using a recursive technique similar to what you might use in Haskell or Lisp:
<body>
{{#countdown n=5}}
<p>item {{this}}</p>
{{/countdown}}
</body>
<template name="countdown">
{{#if positive}}
{{> Template.contentBlock n}}
{{#countdown n=nMinusOne}}
{{> Template.contentBlock this}}
{{/countdown}}
{{/if}}
</template>
Template.countdown.helpers({
positive: function () {return this.n > 0;},
nMinusOne: function () {return this.n - 1;}
});
See meteorpad.
The performance is probably far worse than the usual {{#each}}.
It appears to me that you want to create a <div> for each of an array of IDs (correct me if I'm wrong). This is how I would go about it, no custom iterator necessary:
Template.registerHelper('ids', function(arrayWithIds) {
if (!arrayWithIds) return [];
// do some sorting or whatever with arrayWithIds, for example:
var arrayOfIds = _.map(arrayWithIds, function(obj) {
return obj._id;
});
return arrayOfIds;
});
Then in main.html:
{{#each ids someDataSetWithIds}}
// `someDataSetWithIds` is the helper's parameter
// `this` in each case is an ID
<div id="wow-{{this}}"></div>
{{/each}}
If your helper returns an object, you would use this._id in the template, instead. Did I misunderstand what you're trying to achieve?

Meteor: Passing more than one value to a template

I want to create a template/js combo similar to the ones below. What I would like is to have two variables available to the 'collection' template
<template name="collection">
Title: {{title}}
<UL>
{{#each items}}
{{> item}}
{{/each}}
</UL>
</template>
<template name="collection_items">
<LI>{{item_title}}</LI>
</template>
Where the javascript function would be something like:
Template.collection.data = function() {
var record = Record.findOne({whatever:value});
return { title: record.title, items: record.items }
}
I've tried using Handlebars' {{#with data}} helper and return an object as above, but that just crashed the template. I've tried creating a 'top level' function like:
Template.collection = function () {... }
but that also crashed the template.
What I'm trying to avoid is having two separate functions (one Template.collection.title, and one Template collection.items) where each of them calls a findOne on the Record collection where really its the same template and one call should suffice.
Any ideas?
Template.collection = function () {... }
Template.collection is not a function, it's an instance and thus an object.
You can type Template.collection in the console to see something essential as well as Template.collection. and autocomplete that to see its methods and fields.
For a #with example, the Todos indeed doen't seem to contain one as you have outlined in your comments. So, an example use of it can be found here:
https://github.com/meteor/meteor/blob/master/packages/templating/templating_tests.js#L75
https://github.com/meteor/meteor/blob/master/packages/templating/templating_tests.html#L92
Here is another example that I tried that works on both the current master and devel branch:
<head>
<title>test</title>
</head>
<body>
{{> hello}}
</body>
<template name="hello">
{{#with author}}
<h2>By {{firstName}} {{lastName}}</h2>
{{/with}}
</template>
And the JS part of it:
if (Meteor.is_client) {
Template.hello.author = function () {
return {
firstName: "Charles",
lastName: "Jolley"
};
};
}
Any specific reason why you're hoping to avoid two functions?
From your code sample I see one issue: the first template is calling a second template with this line:
{{> item}}
But your second template is not called 'items'. I believe that your second template should be called this way:
<template name="item">
Seems that it would be simple enough to have helper functions for the first and the second. Although I haven't gotten it to work with my own code, I believe the second helper function would want to use the 'this' convention to refer to the collection you're referring to.
Cheers - holling
Tom's answer is correct. I want to just chime in and add that in my scenario the reason why #with was failing was because due to the 'reactive' nature of meteor my first call to load the model resulted in 'undefined' and I didn't check for it. A fraction later it was loaded ok.
The moral is to do something like
var record = Record.findOne({whatever:value})
if (record) {
return record;
} else {
// whatever
return "loading"
}

Resources