can not detect current language (i18next.language) from handlebar helper? - javascript

I am using following packages for multi-languages solutions.
var i18next = require('i18next');
var i18nFsBackend = require('i18next-node-fs-backend');
var i18nMiddleware = require('i18next-express-middleware');
Since I am using handlebar as my nodejs template engine, that's I can not use i18next t('key') directly in the HTML.
so I created a handlebar helper like following
```javascript
var i18next = require('i18next');
handlebars.registerHelper('t', function(i18n_key) {
console.log(i18next.language)// always undefined, so i18next.t(i18n_key) always return default translation.
var result = i18next.t(i18n_key);
return new handlebars.SafeString(result);
});
```
However, the problem was the function is unable to detect language changed
My Workaround
app.js
```javascript
var i18nextInitCallback = function(error, t){
handlebars.registerHelper('t', function(i18n_key) {
if(app.locals.language !== i18next.language){
i18next.changeLanguage(app.locals.language);
}
var result = i18next.t(i18n_key);
return new handlebars.SafeString(result);
});
};
```
route
```javascript
router.use(function(req, res, next){
res.locals.lng = req.language;
res.app.locals.language = req.language;
next();
});
```
as you can see that on Route I assign res.app.locals.language = req.language;
and then in the handlebar helper function, I use app.locals.language to get the current language and use i18next.changeLanguage() to change the language.
and it worked.
I would like to know if I am doing it right or not?
or if there is a better solution

Using the handle function of the middleware:
app.use(middleware.handle(i18next, {
// options
}));
res.language gets already set for you and a t function fixed to user language of that request.
see: https://github.com/i18next/i18next-express-middleware/blob/master/src/index.js#L48
check out the handlebars sample: https://github.com/i18next/i18next-express-middleware/tree/master/examples/basic-handlebars

Related

How to export get request parameters to another JS file

app.js
app.get('/save', function(req,res){
var switchInput = {
sw1: req.query.switch1,
sw2: req.query.switch2,
sw3: req.query.switch3,
sw4: req.query.switch4,
sw5: req.query.switch5,
sw6: req.query.switch6,
}
console.log(switchInput);
module.exports = switchInput
res.send(switchInput);
});
simulate.js
var mongoose = require('mongoose');
var suit = require('../app')
...
function batteryLife(t){
var elapsed = Date.now() - t;
t_remaining = fullTime - elapsed;
t_battery = secondsToHms(Math.floor(t_remaining/1000));
//console.log(Math.floor(elapsed/1000) + ' s');
console.log(suit.sw1);
return t_battery;
};
Console Log:
{ sw1: 'true',
sw2: 'true',
sw3: 'true',
sw4: 'true',
sw5: 'true',
sw6: 'true' }
--------------Simulation started--------------
undefined
undefined
undefined
undefined
--------------Simulation stopped--------------
When I try to access these values from a different js file they print as undefined I am using postman to simulate values
The values will log from here but print undefined from the other js file
Is there a way to correct this I'm not sure what I am doing wrong
the values are loading into "inputSwitch" but are not coming out on the simulate.js side
First of all, while using youre favorite webserver like Express, you request aka (req) will/could flow amongst middleware before reaching your specific endpoint. Which means your req params are accessible at anytime there which could help you for specific code logic middleware-the-core-of-node-js-apps.
I agree with vibhor1997a, you should not export something there, basically you only module.exports "things" at the end of a file, not something at run-time.
You could do if you really want to deal with switchInput in another file do :
do what vibhor1997a suggest (sync or async function)
have a middleware before reaching your endpoint
raised an event with your switchInput as argument example
You're exporting on an event which isn't a good idea. What you can do instead is call a function in the other file where you need values with the values.
Example
app.js
const simulate = require('./simulate');
app.get('/save', function(req,res){
var switchInput = {
sw1: req.query.switch1,
sw2: req.query.switch2,
sw3: req.query.switch3,
sw4: req.query.switch4,
sw5: req.query.switch5,
sw6: req.query.switch6,
}
simulate(switchInput);
res.send(switchInput);
});
simulate.js
module.exports = function(input){
//have all your functions and code that require input here
function foo(){...}
function bar(){...}
}

Pug call js function from another file inside template

I can't solve this for almost four hours, and i can't find any helpful documentation for this kind of problems. This is the issue, I'm using pug/jade templates and i want to call function inside pug template to transform some data
This is the main template:
/** main template */
section
each pet in pets
.pet
.photo-column
img(src= pet.photo)
.info-column
h2= pet.name
span.species= (pet.species)
p Age: #{calculateAge(pet.birthYear)} //here I need to call calculateAge function
if pet.favFoods
h4.headline-bar Favorite Foods
ul.favorite-foods
each favFood in pet.favFoods
li!= favFood
/** end main template **/
This is the external function:
/** calculateAge.js **/
module.exports = function(birthYear) {
var age = new Date().getFullYear() - birthYear;
if (age > 0) {
return age + " years old";
} else {
return "Less than a year old";
}
};
/** end calculateAge.js **/
What shell I do to make this happen?
There may be better way to handle this, but I usually do it by importing the external module and then passing it as part of template context object. That means the code that renders the template should be something like:
const calculateAge = require("calculateAge"); // change path accordingly
router.get("/main", function(){
let pageInfo = {};
pageInfo.title = "Demo";
pageInfo.calculateAge = calculateAge;
res.render("main", pageInfo);
});
Now, you can access calculateAge in your template. If this module is used a lot in most of the templates, then you should pass it as part of res.locals or app.locals so that it is available for all templates without the need to append it for every path request.
In PUG options use locals property to import functions you want to use inside your templates.
const calculateAge = require('./calculate-age');
const config = {
pug: {
locals: {
calculateAge,
},
},
};
Then you can use it in all your templates like this (please note the extra unescaping tag !{}):
p Age: !{calculateAge(pet.birthYear)}
Like in raw HTML if you want JS to execute in a sepcific part you need a script tag to enclose the js you want to use.
span.species= (pet.species)
p Age:
.script
calculateAge(pet.birthYear)
if pet.favFoods
Make your function available in pug like this:
//assuming you're using express
app.set('view engine', 'pug');
app.locals.someFunction = input => input * 5;
// or
import {someOtherFunction} from "packageOrFile";
app.locals.someOtherFunction = someOtherFunction;
In your pug you then can do
span= someFunction(10)
span= someOtherFunction(123)
This is basically what mahish wrote in his comment, but it actually answers the question satisfactory and here's the documentation.
You can write javascript with .script tag
script.
$( document ).ready(function() {
calculateAge(params)
})

how to avoid 'this' issue in function used as express route callback

I am trying to write a module that creates generic handlers for express routes
e.g.
//create a new route handler with some config
//every routeHanlder method needs to be able to access this config
var handler = new routeHandler({config: "value"});
//handle a get route ("Example 1")
app.get('route', handler.read)
//handle a get route with params ("Example 2")
app.get('route.:id', function(req, res){
handler.read(req,res,{query: {_id: req.params.id}});
});
I am having trouble making "example 1" work...
app.get('route', handler.read)
...as I loose the value of 'this' inside handler.read
I understand why the value of 'this' is different, but I can't figure out how to make it work, or another way to get the desired results without using 'this'.
Here is a plunker link
To summarise I am trying to find a way to make my routeHandler objects (see plunker above, and code paste below) work when used as the callback of an express route (see "example 1" above).
var routeHandler = function(config){
if (!(this instanceof(routeHandler))) {
return new routeHandler(config);
}
config = config || {};
if(config.configData){
this.configData = config.configData;
}
};
routeHandler.prototype = {
read: function(req, res, options){
//The problem: accessing configData without using this
console.log("inside callback", this, this.configData);
options = options || {};
}
};
Edit: I would like the ability to create multiple instances of the route handler with different config data e.g.
var handlerOne = new RouteHandler("configDataOne");
var handlerTwo = new RouteHandler("configDataTwo");
app.get('/firstRoute', handlerOne.read);
app.get('/secondRoute', handlerTwo.read);
You can save routeHandler's configData in express object "app" like below:
app.set("routeHandlerConfigData", "identifier or whatever value you want to store");
then make your routeHandler a simple middleware
var routeHandler = function(req, res, next){
var configData = req.app.get("routeHandlerConfigData");
//Do whatever you want
};
I was inspired by a great comment form yerforkferchips who suggested adding my routerHandler functions inside the constructor like this
this.read = (function read(...) { ... }).bind(this);
Which lets me do exactly what I wanted in my question
app.get('route', handler.read);
BUT i realised that I could use closures in my prototype functions which would sort my 'this' problem and that I would also be able to take in configuration data without having to wrap handler.read in a separate callback function on app.get
RouteHandler.prototype = {
read: function(config){
return function(req, res){
//I have access to req, res and config
}
}
}
so now I can do this
app.get('route', handler.read("configData"));
instead of this
app.get('route', function(req, res){
hander.read(req, res, "configData");
});

How can I register and use a Handlebars helper with Node?

I'm using Handlebars with Node, and that works fine:
require('handlebars');
var template = require('./templates/test-template.handlebars');
var markup = template({ 'some': 'data' });
console.log(markup);
That works fine. However, I need to register and use a custom helper in my template. So, now my code looks like this:
var Handlebars = require('handlebars');
Handlebars.registerHelper('ifEqual', function(attribute, value) {
if (attribute == value) {
return options.fn(this);
}
else {
return options.inverse(this);
}
});
var template = require('./templates/test-template.handlebars');
var markup = template({ 'some': 'data' });
console.log(markup);
But now when I run my script, I get
Error: Missing helper: 'ifEqual'
So: how can I define and use a custom helper in Node?
I figured it out. I needed to do this:
var Handlebars = require('handlebars/runtime')['default'];
One what's really cool is, this even works in the browser with Browserify.
However, I found that an even better way (and probably the "correct" way) is to precompile Handlebars templates via (shell command):
handlebars ./templates/ -c handlebars -f templates.js
Then I do this:
var Handlebars = require('handlebars');
require('./templates');
require('./helpers/logic');
module.exports.something = function() {
...
template = Handlebars.templates['template_name_here'];
...
};
Here is the way i did it.
I guess nowadays its a bit different.
const Handlebars = require('handlebars');
module.exports = function(){
Handlebars.registerHelper('stringify', function(stuff) {
return JSON.stringify(stuff);
});
};
then i made a little script to call require on all the helpers just so they get ran.
// Helpers Builder
let helpersPath = Path.join(__dirname, 'helpers');
fs.readdir(helpersPath, (err, files) => {
if (err) {throw err;}
files.filter((f) => {
return !!~f.indexOf('.js');
}).forEach((jsf) => {
require(Path.join(helpersPath, jsf))();
});
});
OR the simple way
require('./helpers/stringify')();
in fact you dont even have to export it as a function you can just not export anything at all and just call require from another js file with out the function params at the end.

Return generated Typekit id in Yeoman Generator

I'm using node-typekit to create a new Typekit empty font set in my Yeoman generator for use in my project. I am able to successfully create the kit, but cannot figure out how to return the kit id value back to the Yeoman generator so I can add the necessary Typekit script tag values to my web pages. Here is the part of my index.js generator script in question:
At the top of index.js:
var kit = require('typekit');
var typekitID = '';
function setTypekitID(theid) {
typekitID = theid;
};
And the app section:
app: function () {
var token = 'xxxxxxxxxxxxxxxxxxxxxxxx';
var split = this.domainname.split('.');
split.pop();
var localdomain = split.join('.') + '.dev';
kit.create(token, {
name: this.appname,
badge: false,
domains: [this.domainname, localdomain],
families: []
}, function (err, data) {
setTypekitID(data.kit.id);
});
}
If instead of:
setTypekitID(data.kit.id);
I use:
console.log(data.kit.id);
The correct kit ID is displayed in the console. However, I can't quite figure out how to pass the data.kit.id value in the callback back to the generator for further use. Given the current code above, it comes back as "undefined".
Any ideas? Thanks!
Without any experience with typekit, I would guess that kit.create is an asynchronous call. When you make such a call, the generator needs to know about it, so it can delay invocation of further methods until it knows your asynchronous callback has had a chance to execute.
Try doing this:
app: function () {
var done = this.async(); // this tells the generator, "hang on, yo."
// ...
kit.create(token, { /* ... */ }, function (err, data) {
setTypekitID(data.kit.id);
done(); // calling this resumes the generator.
});
}
An example of how the default generator-generator uses this can be found here.

Categories

Resources