6
votes

I'm using handlebars with node.js and express and I have a custom registered helper for temperature display that I'd like to have access to a query parameter from the page URL.

The concept behind the helper is to handle Fahrenheit to Celsius conversion automatically based on whether ?tempFormat=F or tempFormat=C is in the URL or not. Here's the pseudo code for the custom helper I'd like to have:

hbs.registerHelper("formatTemp", function(temp) {
    if (query parameter says to use Fahrenheit) {
        temp = toFahrenheitStr(temp);
    }
    return temp;
});

So, I'd like my template to look like this:

{{#each temperatures}}
    <div class="row {{{stripes @index}}}"> 
        <div class="time">{{prettifyDate t}}</div>
        <div class="atticTemp">{{formatTemp atticTemp}}</div>
        <div class="outsideTemp">{{formatTemp outsideTemp}}</div>
        <div class="spacer"></div>
    </div>
{{/each}}

I'm working around the issue now by pulling request.query.tempFormat and putting it in the data given to the template rendering:

app.get('/debug', function(request, response) {
    var tempData = {
        temperatures: data.temperatures,
        units: request.query.tempFormat || "C"
    };
    response.render('debug', tempData);
});

And, then passing that data in the template:

{{#each temperatures}}
    <div class="row {{{stripes @index}}}"> 
        <div class="time">{{prettifyDate t}}</div>
        <div class="atticTemp">{{formatTemp atticTemp ../units}}</div>
        <div class="outsideTemp">{{formatTemp outsideTemp ../units}}</div>
        <div class="spacer"></div>
    </div>
{{/each}}

But, this seems messy because I have to pass the temperature units to every template render call and I have to then put them in every template where I want them used by a temperature display. They're sitting there in the request object already. So, I'm trying to figure out how to get access to the request object from a handlebars custom helper function? That way, I could save code for every render and save code in each template and have the tempFormat query parameter just automatically apply to any use of formatTemp in my templates.

Does anyone know how to get access to the request object from a handlebars custom helper without using a global?

3
On a related note, does anyone know if there is a forum where handlebars issues are discussed where it might be more likely to find handlebars experts (since there has been no activity on this question)?jfriend00
location.pathname.indexOf('tempFormat=F')raidendev
@raidendev - this isn't a browser, it's a server running node.js and it would have been in location.search, not location.pathname if it was a browser and I'm trying to avoid using globals so multiple requests don't have issues.jfriend00

3 Answers

10
votes

First you need to assign your request object to the response local by registing a middleware anywhere before your route

app.use(function(req,res,next){
    res.local.req = req;
    next();
})

Then you could access the query object in you helper

<div class="atticTemp">{{formatTemp atticTemp ../req.query.tempFormat }}</div>
1
votes

Handlebars always invokes helpers with the current context as this, so you can invoke the block with this to evaluate the block in the current context.

https://handlebarsjs.com/block_helpers.html#basic-blocks

You can combine @Roland's answer with this feature to have your helper determine the correct format with a single parameter.

First add a middleware to inject the value from the request into the context for handlebars.

app.use(function(req, res, next){
    var tempUnits = this.req.query.tempFormat || 'C';
    this.locals.tempUnits = tempUnits;
    next();
})

Then in your middleware function, you can use this.tempUnits to access the value you injected.

hbs.registerHelper("formatTemp", function(temp) {
    var units = this.tempUnits;
    if (units == 'F') {
        temp = toFahrenheitStr(temp);
    }
    return temp;
});

Then you can have your single parameter helper that gets data from the request.

<div class="atticTemp">{{formatTemp atticTemp}}</div>

Note: You can add the whole request object, but it's cleaner to have the middleware inject the value you want.

0
votes

I'm using express-handlebars package. You can register helpers on it's config object as below. Slightly differs from your way but you'll get the idea. To shorten the things, i've just added a second parameter in your helper function (:

var exphbs  = require('express-handlebars');

app.engine('handlebars', exphbs({
    defaultLayout: 'layout',
    helpers : {
        formatTemp: function(temp, unit) {
            console.log(unit);
            if (unit=="F") {
                return temp;
            } else {
                return 32 + (temp* 9 / 5);
            }
        }
    }

context data structure and the usage is the same.

tested.