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)
})
Related
I wanted to write a understandable code in node.js, so I want to put some functions, which are used very often, into other node.js files, and access them from there.
So I get a function, which calls a function from another node.js file and in this other node.js file, also another one is called.
Important to know, if I put all in one file, the code works, so it should be an issue with module export and using functions in another file.
I have one file, getting quotes from a decentralised exchange. Looking like this (quoter_uni_v2.js):
module.exports = function quotes_uni_v2(tokenIn, tokenOut, amountIn, router) {
const quotedAmountOut = router.getAmountsOut(amountIn.toString(), [
tokenIn,
tokenOut,
]);
return quotedAmountOut;
};
And I am importing this function in my second helper file (quotes_5.js) (It is splitted in two files, because in the second one I have to call the function multiple times):
var quotes_uni_v2 = require("./quotes_uni_v2");
module.exports = async function (router1, router2, route, amount_wei) {
console.log(route);
var amount_Out = await quotes_uni_v2.quotes_uni_v2(
route[1],
route[2],
amount_wei,
router1
);
...
return (
Math.round(ethers.utils.formatEther(amount_Out[1].toString()) * 100) / 100
);
};
After that I try to call the function in my main.js:
const quotes_uni_v2 = require("./quotes_uni_v2");
const quotes_5 = require("./quotes_5");
async function calc(route) {
amountOut = await new quotes_5(
quickswap_router,
sushiswap_router,
route,
amount_wei
);
return amountOut;
};
But calling the quotes function does not work... The error is:
TypeError: quotes_5 is not a constructor...
Can someone help me?
Thanks!
i built an app on 1 page, and now i want to oragenize all of its functions and objects in seperate files and directories, and import them to the app.js file. but im having some problems with global objects, the the imported functions dosent recognize them.
example:
//app.js
const req_obj = require("./objectFile.js")
const tool = {
number:4
}
req_obj.obj.addFive() // this will print "cant read number of undefined"
//objectFile.js
const req_func = require("./function.js")
exports.obj = {
addFive:req_func.addFive
}
//function.js
exports.addFive = function(){
return tool.number + 5
}
i know that it might be that im doing all wrong, and this is not how you orgenize an app and call its function. i whould be really glad if some1 will explaine my how to orgenize an app, and call its functions the right way, if what i did is wrong.
The only possibility you have to get access to variables from multiple files is to have them in a separate file and export them like this:
// objects.js
const tool = {
number:4
}
exports.tool = tool
Which then makes your app.js look like:
//app.js
const req_obj = require("./objectFile.js")
const tool = require("./objects.js").tool
// this will read 9
console.log(req_obj.obj.addFive())
And your function.js then becomes:
const tool = require("./objects.js").tool
exports.addFive = function(){
return tool.number + 5
}
Hope it helps.
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
I need to get a template from Ember.TEMPLATES, compile it with a specified object and get its raw HTML value.
Ember.TEMPLATES content (generated using gruntjs) returns a function and seems to be already passed through Handlebars.template() function so for example I would have this:
Ember.TEMPLATES["test"] = Ember.Handlebars.template(function anonymous(Handlebars,depth0,helpers,partials,data) {
this.compilerInfo = [4,'>= 1.0.0'];
helpers = this.merge(helpers, Ember.Handlebars.helpers); data = data || {};
var buffer = '', hashTypes, hashContexts, escapeExpression=this.escapeExpression;
data.buffer.push("<strong>hello world ");
hashTypes = {};
hashContexts = {};
data.buffer.push(escapeExpression(helpers._triageMustache.call(depth0, "test", {hash:{},contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data})));
data.buffer.push("</strong>\n");
return buffer;
});
and would like to compile that template with new values from a JSON object.
I tried something like that based on what I've seen in Ember code:
var test = Ember.TEMPLATES['test'];
var compiled = test({ test: 'value' });
I thought it might work but it doesn't actually.
Basically I'd like to do like with standard handlebars :
Handlebars.compile('<strong>{{hello}}</strong>', { hello: 'world' });
Is there any way to compile a template with specified values, and get the HTML result using Emberjs?
Ember do some modifications in handlebars compiler to enable the use of computed properties, make templates update when model changes etc.
If you see the view render method, it does more than template(context), it use the context and some private custom data. So Handlebars.compile is diferent of Ember.Handlebars.compile and I think that compiled templates from Ember.Handlebars.compile, is not intended to be used outside of a Ember.View.
Marking script types with text/x-raw-handlebars, instead of text/x-handlebars make the template be compiled with Handlebars.compile.
The following sample will work, but without the ember features:
Template
<script type="text/x-raw-handlebars" data-template-name="custom-template">
First name: {{firstName}} <br/>Last name: {{lastName}}
</script>
Javascript
App = Ember.Application.create({
ready: function() {
var template = Ember.TEMPLATES['custom-template'];
var html = template({ firstName: 'Tom', lastName: 'Dale' });
$('body').append(html);
}
});
You can see this sample here http://jsfiddle.net/marciojunior/MC8QB/
I'm loading a template with the following data:
"slides": [
{
"template": "video",
"data": {
"video": ""
}
},
{
"template": "image",
"data": {
"image": ""
}
}
]
in my template I want to loop over these slides and based on the configured template I want to load a partial
{{#each slides}}
{{> resources_templates_overlay_video }}
{{/each}}
How can I make this partial load dynamically (based on the configured template)?
I'm using the require-handlebars-plugin
As far as I can tell, hbs expects the partials to be known at compile time, which is way before you pass in your data. Let's work around that.
First, pull in your dynamic partials before rendering, something like:
// I required the main template here for simplicity, but it can be anywhere
var templates = ['hbs!resources/templates/maintemplate'], l = data.slides.length;
for (var i=0; i<l; i++ )
templates.push('hbs!resources/templates/overlay/'+data[i].template);
require(templates, function(template) {
var html = template(data);
});
And define a helper that will act as a dynamic partial
define(['Handlebars'], function (Handlebars) {
function dynamictemplate(template, context, opts) {
template = template.replace(/\//g, '_');
var f = Handlebars.partials[template];
if (!f) {
return "Partial not loaded";
}
return new Handlebars.SafeString(f(context));
}
Handlebars.registerHelper('dynamictemplate', dynamictemplate);
return dynamictemplate;
});
Finally, modify your main template to look like
{{#each slides}}
{{dynamictemplate this.template this.data}}
{{/each}}
I found the above answers a little hard to understand - they leak globals, have single character variables, and some odd naming. So here's my own answer, for my (and your) reference:
A dynamic partial using 'hbs', express.js default handlebars implementation:
I used this to make a simple blog making (article-name).md into /blog/(article-name), creating a dynamic partial:
// Create handlebars partials for each blog item
fs.readdirSync('blog').forEach(function(blogItem){
var slug = blogItem.replace('.md','')
var fileContents = fs.readFileSync('blog/'+blogItem, 'utf8')
var html = marked(fileContents)
var compiledTemplate = hbs.compile(html);
hbs.registerPartial(slug, compiledTemplate);
})
// Create 'showBlogItem' helper that acts as a dynamic partial
hbs.registerHelper('showBlogItem', function(slug, context, opts) {
var loadedPartial = hbs.handlebars.partials[slug];
return new hbs.handlebars.SafeString(loadedPartial(context));
});
Here's the route. It 404s if the partial doesn't exist, because the blog doesn't exist.
router.get('/blog/:slug', function(req, res){
var slug = req.param("slug")
var loadedPartial = hbs.handlebars.partials[slug];
if ( ! loadedPartial ) {
return res.status(404).json({ error: 'Article not found' })
}
res.render('blog', {
slug: slug
});
})
/views/blog.hbs looks like:
<div class="blog">
{{ showBlogItem slug }}
</div>
When Handlebars.partials[] returns a raw string it means the partial is not compiled.
I am not sure but my best guess is that Handlebars compiles the partial internally when it compiles the template which includes the partial. So when you use a helper to include a partial then Handlebars doesn't recognize it and it will not be compiled.
You can compile the partial yourself. Don't forget to register the compiled partial or you end up compiling every time the partial is required, which hurts performance. Something like this should work.
var template = Handlebars.partials['templatename'],
fnTemplate = null;
if (typeof template === 'function') {
fnTemplate = template;
} else {
// Compile the partial
fnTemplate = Handlebars.compile(partial);
// Register the compiled partial
Handlebars.registerPartial('templatename', fnTemplate);
}
return fnTemplate(context);